線性表(數組、鏈表、隊列、棧)詳細總結

線性表是一種十分基礎且重要的數據結構,它主要包括如下內容:git

  • 數組
  • 鏈表
  • 隊列

接下來,我將對這四種數據結構作一個詳細的總結,其中對鏈表實現了十幾種常見的操做。但願對你有所幫助。github

1.數組

數組(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會自動檢測。函數

2.鏈表

  • 鏈表結點表示
  • 打印單鏈表
  • 單鏈表根據索引插入結點
  • 獲取單鏈表的長度
  • 打印單鏈表的長度
  • 單鏈表刪除指定索引的結點
  • 單鏈表實現元素查找,返回是否存在布爾值
  • 單鏈表刪除指定索引的後續節點
  • 單鏈表反轉
  • 遞歸地進行單鏈表反轉
  • 檢測鏈表中是否含有環
  • 刪除倒數第k個結點
  • 求中間節點
  • 有序鏈表合併

鏈表結點表示

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;
    }

刪除倒數第k個結點

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;
    }

3.棧

  • 順序棧
  • 鏈式棧

1.基於數組實現的順序棧

  • 構造函數
  • 入棧操做
  • 出棧操做
  • 打印操做
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();
    }
}

2.基於鏈表的鏈式棧

  • 入棧操做
  • 出棧操做
  • 打印操做
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();
    }

}

4.普通隊列

  • 基於數組實現的普通隊列
  • 基於鏈表實現的隊列
  • 基於數組實現的循環隊列

1.基於數組實現的普通隊列

  • 構造函數
  • 入隊操做
  • 出隊操做
  • 打印隊列中的元素
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();
    }

}

2.基於鏈表實現的隊列

  • 構造函數
  • 入隊操做
  • 出隊操做
  • 打印隊列中的元素
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();
    }
}

3.基於數組實現的循環隊列

  • 構造函數
  • 入隊操做
  • 出隊操做
  • 打印隊列中的元素
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 送你一份 算法與數據結構思惟導圖。最後,但願咱們一塊兒進步,一塊兒成長!指針

相關文章
相關標籤/搜索