一、数组线性表(ADT)
线性表:又称动态数组,核心是动态数组,可以自行扩容,支持增删改查四种功能
java中有ArrayList也可以自行扩容,二者功能较为相似,且ArrayList也支持转换为数组。
ArrayList list=new ArrayList(); int size=list.size(); //转换为String类型数组 String[] array = (String[])list.toArray(new String[size]); //转换为int类型数组 int[] arr = list.stream().mapToInt(k -> k).toArray();
1.定义成员变量(泛型)
private E[] array;//定义一个泛型数组
private int size;//数组内元素的实际数量
public MyArray(int capacity)
{
this.array服务器托管网=(E[]) new Object[capacity];
this.size=0;//size代表数组里实际有效的数据数量
}
2. 添加元素
添加指定的元素,线性表有可能元素已满,需要扩容
扩容后(或未扩容)再添加元素,并将元素数量+1
public void add(E element)
{//数组按照顺序添加元素
//如果实际元素数量已达到数组长度,判断为线性表已满
//线性表已满则扩容
if(size>=array.length)
resize();
array[size]=element;
size++;
}
3. 插入元素
输入指定元素和要插入的位置下标
需要进行两个判断:
1.需要判断插入的位置是否合法(别超出线性表已有的范围)
2.如果线性表现在已满,就扩容
判断过后,实现插入:for循环解决,给指定位置赋值为输入元素即可
别忘了最后增加元素数量
public void insert(E element,int index) throws IndexOutOfBoundsException {
//插入元素必须按照顺序插入,并且插入次数达到数组长度时才能扩容
if(indexsize)//判断数组是否越界
{
throw new IndexOutOfBoundsException("超出数组范围");
}
if(size>=array.length)//如果实际元素达到数组容量上限
{//数组扩容
resize();
}
//实现数组插入
for (int i = size-1; i >=index; i--) {
array[i+1]=array[i];
}
array[index]=element;//插入元素
size++;
}
4. 删除元素
输入要删除元素的位置下标
需要判断该位置是否超出线性表范围
判断不超出后,执行删除:一个for循环解决
记得让元素数量-1
public void delete(int index) throws IndexOutOfBoundsException
{//删除指定位置的数组元素
if(indexsize)
{
throw new IndexOutOfBoundsException("数组越界,删除失败");
}
for(int i=index;i
5. 线性表扩容
扩容
新建一个数组,数组范围为原数组的两倍
然后把旧数组复制到新数组里
把新数组命名为旧数组的名字(这样就可以继续使用旧数组的名字)
而原旧数组已经被抛弃。
public void resize()
{//数组扩容
E[] newArray=(E[]) new Object[array.length*2];
//把旧数组的内容复制到新的数组中
System.arraycopy(array,0,newArray,0,array.length);
this.array=newArray;
}
6.输出
public void Print()
{
for (int i = 0; i
7.主函数试验
public static void main(String[] args) {
MyArray a=new MyArray(4);
for(int i=0;i
8.完整代码
public class MyArray {
private E[] array;//定义一个泛型数组
private int size;//数组内元素的实际数量
public MyArray(int capacity)
{
this.array=(E[]) new Object[capacity];
this.size=0;//size代表数组里实际有效的数据数量
}
public void add(E element)
{//数组按照顺序添加元素
if(size>=array.length)
resize();
array[size]=element;
size++;
}
//数组插入元素
public void insert(E element,int index) throws IndexOutOfBoundsException {
//插入元素必须按照顺序插入,并且插入次数达到数组长度时才能扩容
if(indexsize)//判断数组是否越界
{
throw new IndexOutOfBoundsException("超出数组范围");
}
if(size>=array.length)//如果实际元素达到数组容量上限
{//数组扩容
resize();
}
//实现数组插入
for (int i = size-1; i >=index; i--) {
array[i+1]=array[i];
}
array[index]=element;//插入元素
size++;
}
public void delete(int index) throws IndexOutOfBoundsException {//删除指定位置的数组元素
if(indexsize)
{
throw new IndexOutOfBoundsException("数组越界,删除失败");
}
for(int i=index;i
二、链表
java自带的List列表结构 void add(E element)在列表末尾添加元素 E get(int index)获取指定索引位置的元素 E set(int index,E element)替换指定索引位置的元素 E remove(int index)移除指定索引位置的元素 int size()返回表的大小我们用自定义类重新实现List的单链表版本
包括:增删改查功能
优点:
线性表逻辑上相邻,物理上也相邻,可随机存取任意元素。
缺点:
线性表插入、删除操作需要移动大量元素
存储空间是预分配的,不灵活,空间浪费,表的存储空间难扩充
1.定义成员变量
java里面没有像C语言的结构体,但java有内部类
新建一个内部类Node表示链表内的单个节点,内部类中包括
1.元素
2.指向下一节点的指针
3.构造函数给元素赋值
构造好节点类,再新建头结点和尾结点指针,并新建size表示链表的有效长度
private static class Node
{//定义一个结点内部类
E data;
Node next;//指向下一结点的指针
Node(E data)
{
this.data=data;
}
}
private Node head;//头结点
private Node last;//尾结点
private int size;//链表实际长度
2.查找节点
输入想要查找的节点下标,返回该下标位置的节点
新建一个指针,从头结点遍历到下标位置,返回指针指到下标位置的节点
public Node get(int index)throws Exception
{//获取指定下标位置的结点
if(index=size)
throw new IndexOutOfBoundsException("超出范围");
Node temp=head;
for (int i = 0; i
3. 插入节点
输入要插入的节点元素值,和要插入的位置下标
新建一个结点存储该结点元素值
先将要插入的节点指针指向插入位置的下一节点
然后再让插入位置的前一节点指向新结点
获取前一节点,可使用第二步定义的“查找节点”方法,输入位置-1下标
插入头结点、尾结点、中间某一节点的三种情况不同
public void insert(E data,int index)throws Exception
{
if(indexsize)
throw new IndexOutOfBoundsException("超出链表表长");
Node insertNode=new Node(data);//定义待插入结点
if(size==0)
{//空链表
head=insertNode;
last=insertNode;
}
else if(index==0)
{//插入到头结点前面
insertNode.next=head;//把原头结点放在待插入元素的下一个位置上
head=insertNode;//把待插入元素放置在头结点上
}
else if(index==size)
{//插入到尾结点后面
last.next=insertNode;//先把待插入结点放在尾结点的下一个位置
last=insertNode;//再把待插入节点设置为尾结点,也就是指针后移了一位
}
else
{//插入中间位置
Node prevNode=get(index-1);//设置插入位置的前结点
insertNode.next=prevNode.next;//先让待插入结点指向插入位置结点
prevNode.next=insertNode;//再让前结点指向待插入结点
}
size++;
}
4. 删除节点
输入要删除节点的下标
如果为空表删不了
新建一个节点指针,指向将要被删除的节点(垃圾桶)
要删除谁,就把谁的值赋给垃圾桶指针,然后再让前一结点指向后一结点
删除头结点、尾结点、中间某一节点情况不同
public void delete(int index)throws Exception
{
if(index=size)
{
throw new IndexOutOfBoundsException("删除失败,超出有效链表长度");
}
if(size==0)
{//空链表
System.out.println("链表中无有效结点,删除失败");
}
Node removeNode=new Node(null);
if(index==0)
{//删除head结点
removeNode=head;//把旧的头结点设置为已删除结点
head=head.next;//把头结点的下一个结点设置为新的头结点
}
else if(index==size-1)
{//删除尾结点
removeNode=last;//把旧的尾结点设置为已删除
Node prevNode=get(size-2);//获取前结点
prevNode.next=null;//让前结点指向空
last=prevNode;//设置前结点为新的尾结点
}
else
{//删除中间节点
Node prevNode=get(index-1);//获取前结点
Node nextNode=prevNode.next.next;//获取删除节点的下一节点
removeNode = prevNode.next;
prevNode.next=nextNode;//让前结点指向后节点
}
size--;
}
5. 输出节点
public void output()
{
Node temp=head;//设置一个结点指针指向头结点
for (int i = 0; i
6.主函数检验
public static void main(String[] args) throws Exception
{
MyList LinkList=new MyList();
LinkList.insert(3,0);//3
LinkList.insert(8,1);//3->8
LinkList.insert(3,2);//3->8->3
LinkList.insert(8,3);//3->8->3->8
LinkList.insert(9,1);//3->9->8->3->8
LinkList.delete(0);//9->8->3->8
LinkList.output();
}
7.完整代码
/*
* java自带的List列表结构
* void add(E element)在列表末尾添加元素
* E get(int index)获取指定索引位置的元素
* E set(int index,E element)替换指定索引位置的元素
* E remove(int index)移除指定索引位置的元素
* int size()返回表的大小*/
/*数据结构就是用自定义类实现List结构:增删改查*/
public class MyList
{
private static class Node{//定义一个结点内部类
E data;
Node next;//指向下一结点的指针
Node(E data)
{
this.data=data;
}
}
private Node head;//头结点
private Node last;//尾结点
private int size;//链表实际长度
//查找节点
public Node get(int index)throws Exception
{//获取指定下标位置的结点
if(index=size)
throw new IndexOutOfBoundsException("超出范围");
Node temp=head;
for (int i = 0; i size)
throw new IndexOutOfBoundsException("超出链表表长");
Node insertNode=new Node(data);//定义待插入结点
if(size==0)
{//空链表
head=insertNode;
last=insertNode;
}
else if(index==0)
{//头插
insertNode.next=head;//把原头结点放在待插入元素的下一个位置上
head=insertNode;//把待插入元素放置在头结点上
}
else if(index==size)
{//尾差
last.next=insertNode;//先把待插入结点放在尾结点的下一个位置
last=insertNode;//再把待插入节点设置为尾结点,也就是指针后移了一位
}
else
{//插入中间位置
Node prevNode=get(index-1);//设置插入位置的前结点
insertNode.next=prevNode.next;//先让待插入结点指向插入位置结点
prevNode.next=insertNode;//再让前结点指向待插入结点
}
size++;
}
//链表删除元素
public void delete(int index)throws Exception{
if(index=size)
{
throw new IndexOutOfBoundsException("删除失败,超出有效链表长度");
}
if(size==0)
{//空链表
System.out.println("链表中无有效结点,删除失败");
}
Node removeNode=new Node(null);
if(index==0)
{//删除head结点
removeNode=head;//把旧的头结点设置为已删除结点
head=head.next;//把头结点的下一个结点设置为新的头结点
}
else if(index==size-1)
{//删除尾结点
removeNode=last;//把旧的尾结点设置为已删除
Node prevNode=get(size-2);//获取前结点
prevNode.next=null;//让前结点指向空
last=prevNode;//设置前结点为新的尾结点
}
else
{//删除中间节点
Node prevNode=get(index-1);//获取前结点
Node nextNode=prevNode.next.next;//获取删除节点的下一节点
removeNode = prevNode.next;
prevNode.next=nextNode;//让前结点指向后节点
}
size--;
}
//输出链表
public void output()
{
Node temp=head;//设置一个结点指针指向头结点
for (int i = 0; i LinkList=new MyList();
LinkList.insert(3,0);
LinkList.insert(8,1);
LinkList.insert(3,2);
LinkList.in服务器托管网sert(8,3);
LinkList.insert(9,1);
LinkList.delete(0);
LinkList.output();
}
}
三、栈
Java自带栈 Stack stack=new Stack(); boolean stack.empty() 测试此堆栈是否为空 E stack.pop() 出栈 E stack.push(E item)入栈 E stack.peek() 获取栈顶元素 int stack.search(Object o)返回对象在堆栈里的位置下标栈是后进先出的,经典例子如:弹夹压弹,羽毛球筒等
后面会附有实际运用的题目
栈的应用场景:
栈的输出顺序和输入顺序相反,所以栈通常用于对“历史”的回溯,也就是逆流而上追溯“历史”
例如实现递归的逻辑,就可以用栈来代替,因为栈可以回溯方法的调用链。
栈还有一个著名的应用场景是面包屑导航,使用户在浏览页面时可以轻松地回
溯到上一级或更上一级页面。
1. 定义成员变量
栈由数组来实现,栈内实际元素的数量用size来记录
private E[] arr;//将元素存到数组中
private int size;//栈的实际长度(元素数量)
public MyStack()
{//调用无参构造方法 默认最大容量12
this(12);
}
public MyStack(int MaxSize) {
this.arr=(E[])new Object[MaxSize];
}
2.判断栈是否为空
public boolean isEmpty()
{//判断是否为空栈
return this.size==0;
}
3. 入栈
元素自栈顶入栈,栈顶为数组[size]
判断栈是否已满,已满就扩容
把元素加入到栈顶,记得有效长度+1
public E push(E value){
//入栈
if(this.size == arr.length)
{//栈满扩容
E[] copyArr=(E[])new Object[arr.length*2];
arr=copyArr;
}
this.arr[size]=value;
this.size++;
return value;
}
4.获取栈顶元素
判断栈空
把栈顶元素返回
public E get()
{//获取栈顶元素
if(isEmpty())
{
System.out.println("栈为空");
return null;
}
return arr[size-1];
}
5.出栈
判断栈空
先把栈顶元素存起来,然后清除栈顶元素
再把栈的有效长度-1
public E pop()
{//出栈
if(isEmpty())
{//栈空抛出异常
throw new RuntimeException("栈中无元素");
}
E top=arr[size-1];//size始终比栈顶元素的下标多1
arr[size]=null;
size--;
return top;
}
6. 返回栈的长度
public int getSize() {
//返回数组大小
return this.size;
}
7. 打印栈
由于栈是只能通过一次出栈一个栈顶元素来遍历
而遍历结束,栈也就空了
因此要新建一个栈,把原栈输出的栈顶元素都存起来
等原栈遍历结束为空栈后,再把新建栈内存的元素再依次遍历入栈到原栈里
这样就保证了打印结束后,原栈内元素不变
public void print()
{
MyStack stack=new MyStack();//新建栈
while(!isEmpty())//遍历到栈空(也可以改成输入要遍历的元素数量)
{
E elem=this.pop();//存储栈顶元素并出栈
//栈顶元素为null说明是数组冗余的部分,非有效元素
if(elem==null)
{
continue;
}
stack.push(elem);//把原栈栈顶元素入栈到新建栈
System.out.print(elem+" ");
}
System.out.println();
while(!stack.isEmpty())
{//把新建栈的栈顶依次赋值给原栈
E elem=stack.pop();
this.push(elem);
}
}
8.主函数检验
public static void main(String[] args) {
MyStack stack=new MyStack();
for (int i = 0; i
9.完整代码
/*
* Java自带栈
* Stack stack=new Stack();
* boolean stack.empty() 测试此堆栈是否为空
* E stack.pop() 出栈
* E stack.push(E item)入栈
* E stack.peek() 获取栈顶元素
* int stack.search(Object o)返回对象在堆栈里的位置下标*/
/*
* java数据结构就是要实现java自带的堆栈的方法
* 了解堆栈实现的原理*/
public class MyStack {
private E[] arr;//将元素存到数组中
private int size;//栈的实际长度(元素数量)
public MyStack()
{//调用无参构造方法 默认最大容量12
this(12);
}
public MyStack(int MaxSize) {
this.arr=(E[])new Object[MaxSize];
}
public boolean isEmpty()
{//判断是否为空栈
return this.size==0;
}
public E get()
{//获取栈顶元素
if(isEmpty())
{
System.out.println("栈为空");
return null;
}
return arr[size-1];
}
public E push(E value){
//入栈
if(this.size == arr.length)
{//栈满扩容
E[] copyArr=(E[])new Object[arr.length*2];
arr=copyArr;
}
this.arr[size]=value;
this.size++;//栈的实际长度,始终比栈顶元素的下标多1
return value;
}
public E pop()
{//出栈
if(isEmpty())
{//栈空抛出异常
throw new RuntimeException("栈中无元素");
}
E top=arr[size-1];
arr[size]=null;
size--;
return top;
}
public int getSize() {
//返回数组大小
return this.size;
}
public void print()
{
MyStack stack=new MyStack();
while(!isEmpty())
{
E elem=this.pop();
if(elem==null)
{
continue;
}
stack.push(elem);
System.out.print(elem+" ");
}
System.out.println();
while(!stack.isEmpty())
{
E elem=stack.pop();
this.push(elem);
}
}
public static void main(String[] args) {
MyStack stack=new MyStack();
for (int i = 0; i
四、队列
JAVA自带Queue队列接口 LinkedList(双向链表)、ArrayList均可实现Queue boolean add(E element): 将指定的元素添加到队列的末尾,如果成功则返回true, 如果队列已满则抛出异常。 boolean offer(E element)同上,如果队列已满返回false; E remove() 移除并返回队列头部元素,如果队列为空抛出异常 E poll() 同上,如果队列为空返回null E element()获取队列头部的元素,但不移除,队列为空抛出异常 E peek() 同上,队列为空返回null int size()返回队列中元素个数 boolean isEmpty() 判断队列是否为空 clear()清空 contains(Object o)是否包含某元素使用自定义类实现队列,满足先进先出的原则,实现循环队列
队列的应用:
队列的输出顺序和输入顺序相同,所以队列通常用于对“历史”的回放,也就 是按照“历史”顺序,把“历史”重演一遍。
例如在多线程中,争夺公平锁的等待队列,就是按照访问顺序来决定线程在队 列中的次序的。
再如网络爬虫实现网站抓取时,也是把待抓取的网站URL存入队列中,再按照存 入队列的顺序来依次抓取和解析的。
1.定义成员变量
队列元素由数组实现,定义一个头部指针和一个尾部指针
队列长度由new MyQueue(int MaxSize)决定
需要注意,由于循环队列的定义,出于判断队空还是队满的区分,只要队不空,头指针和尾指针永远至少隔一个位置
也就是说,队列里的实际元素数量永远比队列长度少1
private E[] arr;
private int front;//头部指针
private int rear;//尾部指针
public MyQueue(int MaxSize)
{
arr=(E[])new Object[MaxSize];
}
2.判断队列是否为空
初始位置,头指针和尾指针都没动
public boolean isEmpty()
{
return rear==front;//头指针等于尾指针,队列无元素
}
3.入队
先判断队列是否已满,判断已满的方法是,(尾指针+1)%队列长度==头指针
如果未满,就为尾指针赋值,需注意,队列为循环队列,尾指针的位置取决于头指针的位置
因此尾指针的位置需要%长度,表示在一个循环里
添加元素后,尾指针+1
public boolean offer(E element) throws Exception {
//判断队列是否已满,已满返回false
if((rear+1)%arr.length==front)
{
System.out.println("队列已满");
return false;
}
arr[rear%arr.length]=element;
rear=(rear+1)%arr.length;//队尾指针后移一位
return true;
}
4. 出队
判断队空
存储队首的元素后,清除队首元素后把头指针后移一位,就能达到出队的效果
public E poll() throws Exception {
//如果队列为空,返回null
if(isEmpty())
{
System.out.println("队列为空");
return null;
}
E element=arr[front];//存储需要出队的元素
arr[front]=null;
front=(front+1)%arr.length;//队头指针后移一位
return element;
}
5.获取队首元素
public E getElement()
{
//判断是否为空,为空返回null
if(isEmpty())
return null;
return arr[front];
}
6.获取队列长度
public int size()
{
return (rear+arr.length-front)% arr.length;
}
7. 清空队列
public boolean clear()
{
E[] newArr=(E[]) new Object[arr.length];
arr=newArr;
front=rear=0;//必须让指针回归零的位置
return true;
}
8. 队列是否包含某元素
public boolean contains(E element)
{
if(isEmpty())
return false;
for (E e : arr) {
if(e==element)
return true;
}
return false;
}
9.输出队列
public void print()
{
for (int i = front; i != rear; i=(i+1)%arr.length) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
10.主函数检验
public static void main(String[] args) throws Exception {
MyQueue myque=new MyQueue(5);
myque.offer(1);//入队rear0->1
myque.offer(2);//rear1->2
myque.offer(3);//rear2->3
myque.offer(4);//rear3->4
myque.poll();//1出队 front0->1
myque.poll();//2出队 front1->2
myque.offer(5);//5入队 rear4->5->0
myque.print();//3 4 5 rear==0 front==2
System.out.println(myque.getElement());//3
System.out.println(myque.contains(4));//true
System.out.println(myque.contains(1));//false
myque.offer(3);//入队 rear0->1+1==front队列已满
myque.offer(8);//
myque.offer(9);
myque.print();//3 4 5 3队列实际可存储元素为arr.length-1个也就是4个
System.out.println(myque.clear());//清空
myque.offer(888);
myque.print();//888
}
11.完整代码
import java.util.Scanner;
/*
* JAVA自带Queue队列接口
* LinkedList(双向链表)、ArrayList均可实现Queue
* boolean add(E element): 将指定的元素添加到队列的末尾,如果成功则返回true,如果队列已满则抛出异常。
* boolean offer(E element)同上,如果队列已满返回false;
* E remove() 移除并返回队列头部元素,如果队列为空抛出异常
* E poll() 同上,如果队列为空返回null
* E element()获取队列头部的元素,但不移除,队列为空抛出异常
* E peek() 同上,队列为空返回null
* int size()返回队列中元素个数
* boolean isEmpty() 判断队列是否为空
* clear()清空
* contains(Object o)是否包含某元素
*/
public class MyQueue
{
private E[] arr;
private int front;//头部指针
private int rear;//尾部指针
public MyQueue(int MaxSize)
{
arr=(E[])new Object[MaxSize];
}
//判断是否为空
public boolean isEmpty()
{
return rear==front;//头指针等于尾指针,队列无元素
}
//入队
public boolean offer(E element) throws Exception {
//判断队列是否已满,已满返回false
if((rear+1)%arr.length==front)
{
System.out.println("队列已满");
return false;
}
arr[rear%arr.length]=element;
rear=(rear+1)%arr.length;//队尾指针后移一位
return true;
}
//出队
public E poll() throws Exception {
//如果队列为空,返回null
if(isEmpty())
{
System.out.println("队列为空");
return null;
}
E element=arr[front];//存储需要出队的元素
arr[front]=null;
front=(front+1)%arr.length;//队头指针后移一位
return element;
}
//获取队首元素
public E getElement()
{
//判断是否为空,为空返回null
if(isEmpty())
return null;
return arr[front];
}
//返回队列中的元素个数
public int size()
{
return (rear+arr.length-front)% arr.length;
}
//清空
public boolean clear()
{
E[] newArr=(E[]) new Object[arr.length];
arr=newArr;
front=rear=0;//必须让指针回归零的位置
return true;
}
//是否包含某元素
public boolean contains(E element)
{
if(isEmpty())
return false;
for (E e : arr) {
if(e==element)
return true;
}
return false;
}
//输出队列
public void print()
{
for (int i = front; i != rear; i=(i+1)%arr.length) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
public static void main(String[] args) throws Exception {
MyQueue myque=new MyQueue(5);
myque.offer(1);//入队rear0->1
myque.offer(2);//rear1->2
myque.offer(3);//rear2->3
myque.offer(4);//rear3->4
myque.poll();//1出队 front0->1
myque.poll();//2出队 front1->2
myque.offer(5);//5入队 rear4->5->0
myque.print();//3 4 5 rear==0 front==2
System.out.println(myque.getElement());//3
System.out.println(myque.contains(4));//true
System.out.println(myque.contains(1));//false
myque.offer(3);//入队 rear0->1+1==front队列已满
myque.offer(8);//
myque.offer(9);
myque.print();//3 4 5 3队列实际可存储元素为arr.length-1个也就是4个
System.out.println(myque.clear());//清空
myque.offer(888);
myque.print();//888
}
}
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net