數據結構之雙向鏈表詳細(java版)

前言

鏈表是一種線性表,常見的線性表還有棧、隊列,本篇主要是分析雙向鏈表的數據結構,以及如何使用java自己實現一個雙向鏈表。在分析雙向鏈表前,有必要先看看單向鏈表的數據結構圖,方便對比雙向鏈表數據結構。

1、單向鏈表

百度百科:單向鏈表(單鏈表)是鏈表的一種,其特點是鏈表的鏈接方向是單向的,對鏈表的訪問要通過順序讀取從頭部開始;鏈表是使用指針進行構造的列表;又稱爲結點列表,因爲鏈表是由一個個結點組裝起來的;其中每個結點都有指針成員變量指向列表中的下一個結點。
在這裏插入圖片描述
刪除操作後:
在這裏插入圖片描述

2、雙向鏈表

百度百科:雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和後繼結點。一般我們都構造雙向循環鏈表。
在這裏插入圖片描述
刪除操作後:
在這裏插入圖片描述

3、手寫實現

public class CoolList<T> {
    //節點個數
    public int size;
    //頭節點
    private SummerNode<T> first;
    //尾節點
    private SummerNode<T> last;

    //定義節點類型
    private static class SummerNode<T> {
        //節點元素
        T t;
        //前一個節點地址
        SummerNode<T> pre;
        //後一個節點地址
        SummerNode<T> next;

        SummerNode(SummerNode<T> pre,T t,SummerNode<T> next){
            this.t = t;
            this.pre = pre;
            this.next = next;
        }
    }

    /**
     * 更新指定位置的元素
     * @param index 位置
     * @param t 元素
     * @return 返回舊值
     */
    public T replace(int index,T t){
        //檢查節點是否存在
        checkExistNode(index);
        //獲取指定位置節點
        SummerNode<T> exist = node(index);
        T oldValue = exist.t;
        //替換新值
        exist.t = t;
        return oldValue;
    }

    /**
     * 在指定位置之前插入元素
     * @param index
     * @param t
     * @return 成功返回true
     */
    public boolean beforeIndexInsert(int index,T t){
        //檢查節點是否存在
        checkExistNode(index);
        //獲取節點
        SummerNode<T> node = node(index);
        //前一個節點
        SummerNode<T> pre = node.pre;
        //插入的節點在pre節點和node節點之間
        final SummerNode<T> newNode = new SummerNode<>(pre, t, node);
        node.pre = newNode;
        if(pre == null){
            //如果插入的位置是第一個節點前
            first = newNode;
        } else {
            pre.next = newNode;
        }
        //元素+1
        size++;
        return true;
    }

    //在末尾插入
    public boolean lastInsert(T t){
        final SummerNode<T> l = last;
        final SummerNode<T> newNode = new SummerNode<>(l, t, null);
        last = newNode;
        if(l == null){
            //如果是首次插入元素,設置爲第一個節點
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;
        return true;
    }

    private void checkExistNode(int index){
        if(index < 0 || index > size){
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size+";");
        }
    }

    /**
     * 獲取指定位置節點元素
     * @param index
     * @return
     */
    public T get(int index){
        checkExistNode(index);
        return node(index).t;
    }

    /**
     * 獲取指定位置的節點
     * @param index
     * @return
     */
    SummerNode<T> node(int index){
        //二分法查找,小於size/2,則順序查找
        if(index < (size >> 1)){
            SummerNode<T> x = first;
            for(int i=0; i<index; i++){
                x = x.next;
            }
            return x;
        } else {
            SummerNode<T> x = last;
            //倒序查找
            for (int i = size-1; i > index; i--){
                x = x.pre;
            }
            return x;
        }
    }

    /**
     * 移除指定位置的節點
     * @param index
     * @return
     */
    public T remove(int index){
        //查找節點是否存在
        checkExistNode(index);
        //刪除節點
        return unlink(node(index));
    }


    /**
     * 移除指定元素的節點
     * @param t
     * @return
     */
    public boolean remove(T t){
        //爲空
        if(t == null){
            for(SummerNode<T> x=first; x!=null; x=x.next){
                //查出爲空的元素,再刪除
                if(x.t == null){
                    unlink(x);
                    return true;
                }
            }
        } else {
            //不爲空,查出不爲空的元素,然後刪除
            for(SummerNode<T> x=first; x!=null; x=x.next){
                if(t.equals(x.t)){
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 刪除節點
     * @param node
     * @return
     */
    private T unlink(SummerNode<T> node){
        final T type = node.t;
        //刪除節點的前一個節點
        final SummerNode<T> pre = node.pre;
        //刪除節點的後一個節點
        final SummerNode<T> next = node.next;
        //頭結點
        if(pre == null){
            first = next;
        } else {
            pre.next = next;
            node.pre = null;
        }
        //尾結點
        if(next == null){
            last = pre;
        } else {
            next.pre = pre;
            node.next = null;
        }
        node.t = null;
        size--;
        return type;
    }
}

測試代碼:

public class CoolListTest {
    public static void main(String[] args) {
        CoolList<String> coolList = new CoolList<>();
        //增加
        coolList.lastInsert("cool");
        coolList.lastInsert("moon");
        coolList.lastInsert("單向鏈表");
        coolList.lastInsert("的");
        coolList.lastInsert("單向鏈表");
        //刪除
        coolList.remove(2);
        //修改
        coolList.replace(coolList.size - 1,"雙向鏈表");
        //插入
        coolList.beforeIndexInsert(1,"summer");
        StringBuffer sb = new StringBuffer();
        for (int i=0; i<coolList.size; i++){
            //查找
            sb.append(coolList.get(i)).append(" ");
        }
        System.out.println(sb);
    }
}

控制檯輸出:

cool summer moon 的 雙向鏈表 

結束語

java中LinkedList容器的底層數據結構也是雙向鏈表,直達地址:從源碼分析java容器之LinkedList

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章