數據結構之雙鏈表

在單鏈表分析中,每個結點只有一個指向後繼結點的next域,如果此時已知當前結點,需要查找其前驅結點,那麼必須從head頭指針遍歷至p的前驅結點,操作效率很低,因此如果p指向前驅結點的next的域,效率就會提升很多,因此就出現了雙鏈表。
雙鏈表是鏈表的一種。它由結點組成,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙鏈表的任意一個結點開始,都可以很方便的訪問他的前驅結點和後繼結點。

雙鏈表的結點示意圖如下:

這裏寫圖片描述

雙鏈表的示意圖如下:

這裏寫圖片描述

雙鏈表的插入操作分析
雙鏈表的插入一般分爲三種情況:
1)插入空雙鏈表

這裏寫圖片描述

2)插入雙鏈表的尾部

這裏寫圖片描述

3)雙鏈表的中間插入

這裏寫圖片描述

從上圖可以看出第一種和第二種情況實際屬於一種情況,只需要注意front.next != null 的情況而第三種情況就不需要注意這種情況,因爲在中間插入的時候無論何時它的後繼結點都不會爲空。

插入操作實際上就是把原來的鏈接打斷,然後把前一個結點的next域指向先插入節點的pre域,將新插入的結點next域指向後一個結點的pre域。

雙鏈表刪除操作分析
雙鏈表的刪除操作也分爲兩種情況:
1)雙鏈表的尾部刪除操作

這裏寫圖片描述

2)雙鏈表的中間結點刪除操作

這裏寫圖片描述

第一種情況只要注意p.next.pre拋空指針的情況,而對於第二種情況不需要關心。

雙鏈表的刪除操作實際就是把要刪除結點p的pre域和next域都斷開,然後把p前後的結點連接在一起。

雙鏈表的查詢分析:
雙鏈表的查詢操作與單鏈表的查詢操作相似,只需要查找的當前結點獲取他的data即可。

循環雙鏈表就是雙鏈表的最後一個結點的next域指向頭結點,而頭結點的prev指針指向最後一個結點。

在循環雙鏈表中我們不再需要尾指向結點,因爲整個鏈表已構成循環,在頭結點head的位置也可以輕鬆獲取到尾部結點的位置。對於循環雙鏈表的插入、刪除操作也無需區分位置操作的情況,這是由於循環雙鏈表的本身的特殊性,使p.next.pre永遠不可能爲null,因此我們在插入和刪除時代碼實現相對簡單些。

用 java 實現鏈表如下:

Step1.定義結點

package test;

/**
 * Created by DELL on 2017/12/1.
 */
public class Dnode<T> {
    public T data;
    public Dnode<T> prev;//前繼指針
    public Dnode<T> next;//後繼指針

    public Dnode(T data, Dnode<T> prev, Dnode<T> next){
        this.data = data;
        this.next = next;
        this.prev = prev;
    }
    public Dnode(T data){
        this(data,null,null);
    }
    public Dnode(){
        this(null,null,null);
    }
    public String toString(){
        return this.data.toString();
    }
}

Step2.定義一個雙鏈表

package test;

/**
 * Created by DELL on 2017/12/1.
 */
public class DoubleLinkedList<T>  {
    private Dnode<T> head ;//不帶數據的頭結點
    private int size;//鏈表的大小

    public DoubleLinkedList() {
       head = new Dnode<>(null,null,null);
       head.prev = head.next = head;
       size = 0;
    }
    //返回結點數目
    public int count(){
        return size;
    }
    //返回鏈表是否爲空
    public boolean isEmpty(){
        return size == 0;
    }
    //獲取第index位置的結點
    public Dnode<T> getNode(int index){
        if(index<0 || index >= size){
            throw new IndexOutOfBoundsException();
        }
        //正向查找
        if(index <= size/2){
            Dnode<T> node = head.next;
            for(int i=0;i < index; i++){
                node = node.next;
            }
            return node;
        }
        //反向查找
        Dnode<T> rnode = head.next;
        int rindex = size - index - 1;
        for(int j=0;j<index;j++){
            rnode = rnode.prev;
        }
        return rnode;
    }
    //獲取第index位置的結點的值
    public T get (int index){
        return getNode(index).data;
    }
    //獲取第一個結點的值
    public T getFirst(){
        return getNode(0).data;
    }
    //獲取最後一個結點的值
    public T getLast(){
        return getNode(size-1).data;
    }
   //將結點插入插入到第index位置之前
    public void insert(int index , T t){
        if(index == 0){
            Dnode<T> node = new Dnode<T>(t,head,head.next);
            //讓head的後繼結點的prev域指向新插入的結點
            head.next.prev = node;
            head.next = node;
            //鏈表長度加1
            size ++ ;
            return ;
        }
        //獲得該位置的結點
        Dnode<T> inode = getNode(index);
        //定義一個新插入的結點
        Dnode<T> tnode = new Dnode<T>(t, inode.prev, inode);
        //讓該位置上的結點的前繼結點的next域指向新插入的結點
        inode. prev.next = tnode;
        inode.next = tnode;
        size++;
        return ;
    }
    //將結點插入到第一個結點處
    public void insertFist(T t){
        insert(0,t);
    }
    //將結點追加到鏈表的末尾
    public void insertLast(T t){
        Dnode dnode = new Dnode<T>(t,head.prev,head);
        //head的前繼結點就是鏈表的尾結點
        head.prev.next = dnode;
        head.prev = dnode;
        size++;
    }
    //刪除index位置的節點
    public void del(int index){
        Dnode<T> inode = getNode(index);
        //該位置上的結點的前繼結點的next域指向該位置結點的後繼結點
        inode.prev.next = inode.next;
        //該位置上的結點的後繼結點的prev域指向該位置結點的前繼結點
        inode.next.prev = inode.prev;
        inode = null;
        size -- ;
    }
    //刪除第一個結點
    public void delFist(){
        del(0);
    }
    //刪除最後一個結點
    public void delLast(){
        del(size-1);
    }
    public static void main (String[] args){
        System.out.println("鏈表的插入操作");
        int[] array = {10,20,30,40};
        //創建雙鏈表
        DoubleLinkedList<Integer> doubleLinkedList = new DoubleLinkedList<>();
        doubleLinkedList.insert(0,20);//將20插入到第一個位置
        doubleLinkedList.insertLast(10);//將10插入到鏈表末尾
        doubleLinkedList.insertFist(30);//將30插入鏈表的第一個位置
        for(int i=0;i<doubleLinkedList.size;i++){
            //輸出鏈表的值
            System.out.println(doubleLinkedList.get(i));
        }
        System.out.println("鏈表的大小爲:"+doubleLinkedList.count());
        System.out.println("…………………………………………………………");
        System.out.println("鏈表的查找操作");
        System.out.println("獲取第一個結點的值:"+doubleLinkedList.getFirst());
        System.out.println("獲取最後一個結點的值:"+doubleLinkedList.getLast());
        System.out.println("獲取任意結點的值:"+doubleLinkedList.getNode(2));
        System.out.println("………………………………………………………………");
        System.out.println("鏈表的刪除操作");
        System.out.println("刪除第一個結點後的鏈表的值");
        doubleLinkedList.delFist();
        for(int i=0;i<doubleLinkedList.size;i++){
            //輸出鏈表的值
            System.out.println(doubleLinkedList.get(i));
        }
      //其他的刪除方法類似,直接調用即可。

    }
}
發佈了46 篇原創文章 · 獲贊 49 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章