手寫代碼:LinkedList


一、鏈表特點以及原理

查詢慢,增刪快。

這個分爲兩種情況,在頭尾增加,查詢、和在中間增刪,查詢;

在頭尾進行:

在頭尾進行增刪,直接在存儲頭尾對象的first、last添加上新加的元素即可(效率極高);

在頭尾進行查詢,因爲內部優化,大於一半從後往前查詢,效率方面也是可以的。

在中間進行:

在中間查詢,需要把位數走一遍,也就是需要查詢索引5的內容,就要創建(5-1)次的對象,時間及其慢。

在中間刪除,要把上面查詢的步驟走一遍以後,在把指定的元素刪除,再把其前後兩個對象(鏈表),連接起來。

二、單個實現原理解析

1、添加元素add方法(添加內容)

分爲添加第一個元素和其它元素兩種:

第一個元素: 存儲的首個元素(first)和末尾元素(last)都是當前設置的元素;

其它元素: 先將兩個元素進行綁定、設置最後一個元素的next爲null,存儲的最後一個元素爲當前元素;

2、toString方法

利用StringBuilder創建對象,從first元素遞歸獲取每一個存儲的對象(element)值,存儲進StringBuilder中。

3、get方法(根據索引)

調用7、檢測索引,調用4、獲取指定對象,並從對象中獲取元素(element)的值;

4、獲取指定索引的對象(索引)

分兩種情況,索引大於有效長度一半(size),從後側開始檢索,不滿足從左側;

根據當前對象存儲的下一個對象的內容,進行遞歸判斷,直到找到自己想要的次數對象爲止;

5、remove方法(刪除指定索引位置元素)

調用4、獲取指定對象,分情況進行刪除(1、開頭;2、中間位置;3、末尾位置),長度減一(size–)

開頭: 根據當前元素獲取後一個元素對象,設置存儲的首個元素(first)爲後一個元素;

中間: 獲取當前元素前後兩個元素對象,並把這兩個元素進行連接;

末尾: 根據當前元素獲取前一個元素對象,設置存儲的末尾元素(last)爲前一個元素;

6、add方法(根據索引添加到指定位置)

調用4、獲取指定對象,分情況進行添加(1、開頭;2、中間位置;3、末尾位置),長度加一(size++)

開頭: 創建兩個元素連接,設置存儲的首個元素(first)爲當前對象;

中間: 獲取當前元素前一個元素對象,將前一個元素與當前元素連接,將當前元素與後一個元素連接;

末尾: 創建兩個元素連接,設置存儲的末尾元素(last)爲當前對象;

7、索引檢測

判斷索引的數組內容進行判斷,不滿足則拋出運行時異常(RuntimeException)

三、完整增加步驟

1、創建基礎結構,添加add、toString方法

Node存儲元素對象

public class Node {
    Node privious;  //上一個節點
    Node next;      //下一個節點
    Object element;   //元素數據

    public Node(Node privious, Node next, Object element) {
        this.privious = privious;
        this.next = next;
        this.element = element;
    }

    public Node(Object element) {
        this.element = element;
    }
}

MyLinkedList工具類

public class MyLinkedList {

    private Node first;
    private Node last;

//    1、添加元素add方法
    public void add(Object obj) {
        Node node = new Node(obj);
        
        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的開頭爲上一個元素對象(最後一個對象)
            node.privious = last;
//            元素的最後爲null
            node.next = null;
//            上一個元素設置爲後一個對象
            last.next = node;
//            設置最後元素爲現在的對象
            last = node;
        }
    }

//    2、toString方法
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        Node temp = first;
        while (temp != null) {
            sb.append(temp.element + ",");
            temp = temp.next;
        }
        sb.setCharAt(sb.length() - 1, ']');
        return sb.toString();
    }

調用測試:

    public static void main(String[] args) {
        MyLinkedList list = new MyLinkedList();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        String s = list.toString();
        System.out.println(s);
    }
}

難點解析:

是設置新創建的元素的對象裏面的,前一個元素地址(privious)和後一個元素地址(next)

//元素的開頭爲上一個元素對象(最後一個對象)
node.privious = last;
//元素的最後爲null
node.next = null;

是設置創建的上一個元素裏面的,後一個元素地址(next)的值,其中last存儲的上一個元素對象的地址值;

//上一個元素設置爲後一個對象
last.next = node;
//設置最後元素爲現在的對象
last = node;

2、增加get方法

增加size變量,並添加size的增加方法

    private int size;

    //    1、添加元素add方法
    public void add(Object obj) {
        Node node = new Node(obj);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的開頭爲上一個元素對象(最後一個對象)
            node.privious = last;
//            元素的最後爲null
            node.next = null;
//            上一個元素設置爲後一個對象
            last.next = node;
//            設置最後元素爲現在的對象
            last = node;
        }
        size++;
    }

增加get方法

    //    3、get方法
    public Object get(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引數字不合法" + index);
        }
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近與開頭位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近於末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp.element;
    }

3、get方法優化、增加remove方法

get方法優化

    //    3、get方法
    public Object get(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引數字不合法" + index);
        }
        //調用封裝的getNode方法獲取指定索引位置對象
        Node temp = getNode(index);
        return temp != null ? temp.element : null;
    }

    //    4、封裝的獲取指定索引的對象
    public Node getNode(int index) {
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近與開頭位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近於末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp;
    }

增加remove方法

    //        5、remove方法
    public void remove(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引數字不合法" + index);
        }

        Node temp = getNode(index);
        if (temp != null) {
            Node before = temp.privious;
            Node after = temp.next;

            if (before != null) {
                before.next = after;
            }
            if (after != null) {
                after.privious = before;
            }
//            被刪除的元素是第一個元素時
            if (index == 0) {
                first = after;
            }
//            被刪除的元素時最後一個元素時
            if (size == index - 1) {
                last = before;
            }
            size--;
        }
    }

4、根據索引添加元素

//    6、add方法
    public void add(int index,Object obj){
        if (index<0||index>size-1){
            throw new RuntimeException("請輸入指定範圍索引 "+index);
        }
        Node newNode = new Node(obj);
        Node temp = getNode(index);

        if (temp!=null){
            Node before = temp.privious;
//            1、添加的元素在第一個時
            if (index==0){
                newNode.next=temp;
                temp.privious=newNode;
                first=newNode;
            }
//            2、添加的元素在最後一個時
            if (index==size-1){
                newNode.privious=temp;
                temp.next=newNode;
                last=newNode;
                size++;
                return;
            }
//            3、添加的元素在普通位置時
            if (before!=null){
//                新建元素與前一個創建連接
                before.next=newNode;
                newNode.privious=before;
//                新建元素與後一個創建連接
                newNode.next=temp;
                temp.privious=newNode;
            }
            size++;
        }
    }

5、增加索引檢測、泛型

索引檢測:

    //    7、索引檢測
    private void checkRange(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引數字不合法: " + index);
        }
    }

泛型:

public class MyLinkedList<E> {

    private Node first;
    private Node last;
    private int size;

    //    1、添加元素add方法
    public void add(E element) {
        Node node = new Node(element);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的開頭爲上一個元素對象(最後一個對象)
            node.privious = last;
//            元素的最後爲null
            node.next = null;
//            上一個元素設置爲後一個對象
            last.next = node;
//            設置最後元素爲現在的對象
            last = node;
        }
        size++;
    }

四、完整版代碼

public class MyLinkedList<E> {
    
    private Node first;     //存儲第一個元素對象
    private Node last;      //存儲最後一個元素對象
    private int size;       //有效的索引長度(存有值的位數)

    //    1、添加元素add方法(添加內容)
    public void add(E element) {
        Node node = new Node(element);

        if (first == null) {
            first = node;
            last = node;
        } else {
//            元素的開頭爲上一個元素對象(最後一個對象)
            node.privious = last;
//            元素的最後爲null
            node.next = null;
//            上一個元素設置爲後一個對象
            last.next = node;
//            設置最後元素爲現在的對象
            last = node;
        }
        size++;
    }

    //    2、toString方法
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        Node temp = first;
        while (temp != null) {
            sb.append(temp.element + ",");
            temp = temp.next;
        }
        sb.setCharAt(sb.length() - 1, ']');
        return sb.toString();
    }

    //    3、get方法(根據索引)
    public E get(int index) {
//        檢測索引
        checkRange(index);
        //調用封裝的getNode方法獲取指定索引位置對象
        Node temp = getNode(index);
        return temp != null ? (E)temp.element : null;
    }

    //    4、獲取指定索引的對象
    private Node getNode(int index) {
//        檢測索引
        checkRange(index);
        Node temp = null;
        if (index <= (size >> 1)) { //索引位置靠近與開頭位置
            temp = first;
            for (int i = 0; i < index; i++) {
                temp = temp.next;
            }
        } else { //索引位置靠近於末尾位置
            temp = last;
            for (int i = size - 1; i > index; i--) {
                temp = temp.privious;
            }
        }
        return temp;
    }

    //        5、remove方法(刪除指定索引位置元素)
    public void remove(int index) {
//        檢測索引
        checkRange(index);

        Node temp = getNode(index);
        if (temp != null) {
            Node before = temp.privious;
            Node after = temp.next;

            if (before != null) {
                before.next = after;
            }
            if (after != null) {
                after.privious = before;
            }
//            被刪除的元素是第一個元素時
            if (index == 0) {
                first = after;
            }
//            被刪除的元素時最後一個元素時
            if (size == index - 1) {
                last = before;
            }
            size--;
        }
    }

    //    6、add方法(根據索引添加到指定位置)
    public void add(int index, E element) {
//        檢測索引
        checkRange(index);

        Node newNode = new Node(element);
        Node temp = getNode(index);

        if (temp != null) {
            Node before = temp.privious;
//            1、添加的元素在第一個時
            if (index == 0) {
                newNode.next = temp;
                temp.privious = newNode;
                first = newNode;
            }
//            2、添加的元素在最後一個時
            if (index == size - 1) {
                newNode.privious = temp;
                temp.next = newNode;
                last = newNode;
                size++;
                return;
            }
//            3、添加的元素在普通位置時
            if (before != null) {
//                新建元素與前一個創建連接
                before.next = newNode;
                newNode.privious = before;
//                新建元素與後一個創建連接
                newNode.next = temp;
                temp.privious = newNode;
            }
            size++;
        }
    }

    //    7、索引檢測
    private void checkRange(int index) {
        if (index < 0 || index > size - 1) {
            throw new RuntimeException("索引數字不合法: " + index);
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章