线性表是一种十分基础且重要的数据结构,它主要包括如下内容:git
接下来,我将对这四种数据结构作一个详细的总结,其中对链表实现了十几种常见的操做。但愿对你有所帮助。github
数组(Array)是一种线性表数据结构。它用一组连续的内存空间,来存储一组具备相同类型的数据。
注意点:①.数组是一种线性表;②.连续的内存空间和相同类型的数据
因为第二个性质,数组支持 “随机访问”,根据下表随机访问的时间复杂度为O(1);但与此同时却使得在数组中删除,插入数据须要大量的数据搬移工做。算法
假如数组的长度为n,咱们须要将一个数据插入到数组的第k个位置,咱们须要将第k~n位元素都顺序地日后挪动一位。
最好状况时间复杂度为O(1),此时对应着在数组末尾插入元素;
最坏状况时间复杂度为O(n),此时对应着在数组开头插入元素;
平均状况时间复杂度为O(n),由于咱们在每一个位置插入元素的几率相同,故(1+2+3+……+n)/n=O(n);
可是根据咱们的需求,有一个特定的场景。若是数组的数据是有序的,那么咱们在插入时就必定要那么作;可是若是数组中存储的数据并无任何规律,数组只是被当成一个存储数据的集合,咱们能够有一个取巧的方法:
直接将第k个元素搬移到数组元素的最后,把新的数据直接放入第k个位置便可(是否是很简单啊),这时插入元素的复杂度为O(1)。数组
和插入操做同样,为了保证内存的连续性,删除操做也须要搬移数据。
最好状况时间复杂度为O(1),此时对应着删除数组末尾的元素;
最坏状况时间复杂度为O(n),此时对应着删除数组开头的元素;
平均状况时间复杂度为O(n),由于咱们删除每一个位置的元素的几率相同,故(1+2+3+……+n)/n=O(n);
固然,在某些特殊状况下,咱们并不必定非要进行复杂的删除操做。咱们只是将须要删除的数据记录,而且伪装它以经被删除了。直到数组没有更多空间存储数据时,咱们再触发一次真正的删除操做便可。数据结构
这其实就和生活中的垃圾桶相似,垃圾并无消失,只是被“ 标记”成了垃圾,而直到垃圾桶塞满时,才会清理垃圾桶。
在 C 语言中,只要不是访问受限的内存,全部的内存空间都是能够自由访问的。若是疏忽会形成严重的后果。固然,Java会自动检测。函数
public class Node{ int data; Node Next; }
public class Method { //打印单链表 public static void PrintNode (Node list){ for(Node x=list;x!=null;x=x.Next) System.out.print(x.data+" "); System.out.println(); }
public static Node insert(Node first,int index,Node a){ Node ret = new Node(); ret.Next=first;//建立一个虚拟头节点 Node p=ret; while((index--)!=0) p=p.Next; //完成节点的插入操做 a.Next=p.Next; p.Next=a; //返回真正的链表头节点地址 return ret.Next;//函数返回链表的头节点 }
public static int GetLength(Node first){ int n=0; for(Node x=first;x!=null;x=x.Next){ ++n; } return n; }
public static void PrintLength(Node first){ System.out.println("Length : "+GetLength(first)); }
public static Node Delete(Node first,int index){ if(index<0||index>=GetLength(first)) return first; else{ Node ret=new Node(); ret.Next=first; Node p=ret; while((index--)!=0) p=p.Next; //完成节点的删除操做 p.Next=p.Next.Next; return ret.Next; } }
public static boolean Find(Node first,int key){ for(Node x=first;x!=null;x=x.Next){ if(x.data==key) return true; } return false; }
public static void RemoveAfter(Node first,int index){ Node ret=new Node(); ret.Next=first; Node p=ret; while((index--)!=0) p=p.Next; p.Next.Next=null; }
public static Node reverse(Node list){ Node curr=list,pre=null; while(curr!=null){ Node next=curr.Next; curr.Next=pre; pre=curr; curr=next; } return pre; }
public static Node reverseRecursively(Node head){ if(head==null||head.Next==null) return head;//递归的终止条件,返回反转后链表的头节点 Node reversedListHead=reverseRecursively(head.Next); head.Next.Next=head;//改变这两个结点之间的指向顺序 head.Next=null; return reversedListHead;//返回反转后的链表头节点 }
public static boolean checkCircle(Node list){ if(list==null) return false; Node fast=list.Next; Node slow=list; while(fast!=null&&fast.Next!=null){ fast=fast.Next.Next; slow=slow.Next; if(slow==fast) return true; } return false; }
public static Node deleteLastKth(Node list,int k){ //利用两个指针,fast和slow,它们之间差k个位置,判断若是fast.Nest=null,也就表明着slow这个位置就是倒数第k个结点 Node fast=list; int i=1; while(fast!=null&&i<k){ fast=fast.Next; ++i; } if(fast==null) return list; Node slow=list; Node prev=null; while(fast.Next!=null){ fast=fast.Next; prev=slow; slow=slow.Next; } if(prev==null){ list=list.Next; }else{ prev.Next=prev.Next.Next; } return list; }
public static Node findMiddleNode(Node list){ if(list==null) return null; Node fast=list; Node slow=list; while(fast!=null&&fast.Next!=null){ fast=fast.Next.Next; slow=slow.Next; } return slow; }
public static Node mergeTwoLists(Node l1,Node l2){ Node soldier=new Node(); Node p=soldier; while(l1!=null&&l2!=null){ if(l1.data<l2.data){ p.Next=l1; l1=l2.Next; } else{ p.Next=l2; l2=l2.Next; } p=p.Next; } if(l1!=null){ p.Next=l1;} if(l2!=null){ p.Next=l2;} return soldier.Next; }
package Stack; //基于数组实现的顺序栈 public class ArrayStack { private int[] items; private int count;//栈中的元素个数 private int n;//栈的大小 //初始化数组,申请一个大小为n的数组空间 public ArrayStack(int n){ this.items=new int[n]; this.n=n; this.count=0; } //入栈操做 public boolean push(int item){ //数组空间不足,直接返回false,入栈失败 if(count==n) return false; //将data放在下标为count的位置,而且count加一 items[count]=item; ++count; return true; } //出栈操做 public int pop(){ //栈为空,直接返回-1; if(count==0) return -1; //返回下标为count-1的数组元素,而且栈中元素个数count减一 int tmp=items[count-1]; --count; return tmp; } public void PrintStack(){ for(int i=count-1;i>=0;--i){ System.out.print(items[i]+" "); } System.out.println(); } }
package Stack; public class LinkedListStack { private Node top;//栈顶(最近添加的元素) private int N;//元素数量 private class Node{ //定义告终点的嵌套类 int data; Node Next; } public boolean isEmpty(){ return top==null; } public int size(){ return N; } public void push(int data){ /*Node newNode=new Node(); //判断是否为空栈 //if(top==null) newNode=top; top.data=data; top.Next=newNode; N++;*/ Node newNode=top; top=new Node(); top.data=data; top.Next=newNode; ++N; } public int pop(){ //从栈顶删除元素 if(top==null) return -1;//这里用-1表示栈中没有数据 int data=top.data; top=top.Next; N--; return data; } public void PrintStack(){ for(Node x=top;x!=null;x=x.Next){ System.out.print(x.data+" "); } System.out.println(); } }
package Queue; //用数组实现队列 public class ArrayQueue { //数组:items,数组大小:n private int[] items; private int n=0; //head表示队头下标,tail表示队尾下标 private int head=0; private int tail=0; //申请一个大小为capacity的数组 public ArrayQueue(int capacity){ items=new int[capacity]; n=capacity; } //入队(一),基础版 public boolean enqueue(int item){ //若是tail==n,表示队列末尾已经没有空间了 if(tail==n) return false; items[tail]=item; ++tail; return true; } //入队(二),改进版 public boolean enqueue1(int item){ //若是tail==n,表示队列末尾已经没有空间了 if(tail==n){ //tail==n&&head==0,表示整个队列都占满了 if(head==0) return false; //数据搬移 for(int i=head;i<tail;++i){ items[i-head]=items[i]; } //搬移完成后从新更新head和tail tail=tail-head; head=0; } items[tail]=item; ++tail; return true; } //出队 public int dequeue(){ //若是head==tail,表示队列为空 if(head==tail) return -1;//这里用-1表示队列为空 int ret=items[head]; ++head; return ret; } //打印队列 public void PrintQueue(){ for(int i=head;i<tail;++i){ System.out.print(items[i]+" "); } System.out.println(); } }
package Queue; //基于链表实现的队列 public class LinkedListQueue { private Node head;//指向最先添加的结点的连接 private Node tail;//指向最近添加的结点的连接 private int N;//队列中的元素数量 private class Node{ //定义告终点的嵌套类 int data; Node Next; } public boolean isEmpty(){ return head==null; } public int size(){ return N;} //向表尾添加元素,即入队 public void enqueue(int data){ Node newNode=tail; tail=new Node(); tail.data=data; tail.Next=null; if(isEmpty()) head=tail; else newNode.Next=tail; ++N; } public int dequeue(){ //从表头删除元素 int data=head.data; head=head.Next; if(isEmpty()) tail=null; N--; return data; } //打印输出队列元素 public void PrintQueue(){ for(Node x=head;x!=null;x=x.Next){ System.out.print(x.data+" "); } System.out.println(); } }
package Queue; public class CircularQueue { //数组items,数组大小n private int[] items; private int n=0; //head表示队头下标,tail表示队尾下标 private int head=0; private int tail=0; //申请一个大小为capacity的数组 public CircularQueue(int capacity){ items = new int[capacity]; n=capacity; } //入队 public boolean enqueue(int item){ //队列满了 if((tail+1)%n==head) return false; items[tail]=item; tail=(tail+1)%n;//实现计数的循环 return true; } //出队 public int dequeue(){ //若是head==tail,表示队列为空 if(head==tail) return -1;//以-1表示队列为空 int ret=items[head]; head=(head+1)%n; return ret; } //打印队列 public void PrintQueue(){ if(n==0) return; for(int i=head;i%n!=tail;i=(i+1)%n){ System.out.print(items[i]+" "); } System.out.println(); } }
文章代码太多,我原本是但愿分红几篇文章写的,可是因为一些缘由,最终放在了一块儿,略显臃肿。代码都是通过测试用例测试过的,应该不会有错误。测试
若是体验不太好,能够移步个人Github,里面观感较好。this
题外话:对于算法初学者,推荐一本十分 nice 的书籍 《算法第四版》,里面各类配图十分详细。若是你须要电子版文件,后台回复算法4便可得到下载连接。后台回复 算法01 送你一份 算法与数据结构思惟导图。最后,但愿咱们一块儿进步,一块儿成长!指针