主頁 > 軟體設計 > 線性結構:鏈表

線性結構:鏈表

2021-03-07 15:12:17 軟體設計

目錄

  • 第一章 單向鏈表介紹
  • 第二章 單向鏈表實作
    • 2.01、定義鏈表的結點類
    • 2.02、定義鏈表的屬性值
    • 2.03、初始化各個屬性值
    • 2.04、獲取鏈表當前大小
    • 2.05、判斷鏈表是否為空
    • 2.06、檢查下標是否合法
    • 2.07、連接鏈表兩個結點
    • 2.08、釋放鏈表指定結點
    • 2.09、回傳鏈表最后結點
    • 2.10、回傳鏈表首個結點
    • 2.11、獲取指定位置結點
    • 2.12、獲取位置之前結點
    • 2.13、鏈表尾后添加資料
    • 2.14、鏈表頭后添加資料
    • 2.15、指定位置添加資料
    • 2.16、洗掉指定位置結點
    • 2.17、洗掉鏈表首個結點
    • 2.18、洗掉鏈表最后結點
    • 2.19、輸出鏈表所有結點
    • 2.20、反轉鏈表所有結點
    • 2.21、清空鏈表所有結點
    • 2.22、順序查找資料首次出現位置
    • 2.23、逆序查找資料首次出現位置
  • 第三章 雙向鏈表介紹
  • 第四章 雙向鏈表實作
    • 4.01、且看代碼如此多嬌
    • 4.02、獲取位置之前結點
    • 4.03、洗掉鏈表最后結點
    • 4.04、逆序查找資料首次出現位置


專案地址:https://gitee.com/caochenlei/data-structures

第一章 單向鏈表介紹

鏈表是一種物理存盤單元上非連續、非順序的存盤結構,資料元素的邏輯順序是通過鏈表中的指標連接次序實作的,鏈表由一系列結點組成,結點可以在運行時動態生成,每個結點包括兩個部分:一個是存盤資料元素的資料域,另一個是存盤下一個結點地址的指標域,

單向鏈表(單鏈表)是鏈表的一種,其特點是鏈表的連接方向是單向的,對鏈表的訪問要從頭部開始順序讀取,head指標指向第一個結點又稱為頭結點,而終止于最后一個指向null的結點,鏈表的頭結點的資料域不存盤資料,而頭結點的指標域指向第一個真正存盤資料的結點,這里為了方便操作,我又增加了一個last結點,這個結點就是為了指向最后一個結點,節約操作時間,提升查詢性能,

第二章 單向鏈表實作

2.01、定義鏈表的結點類

/**
 * 單向鏈表實作代碼
 */
public class SinglyLinkedList<E> {
    //定義結點類
    public class Node {
        E data;     //代表結點資料
        Node next;  //指向下個結點

        public Node(E data) {
            this.data = data;
        }

        @Override
        public String toString() {
            return "Node{data=" + data + "}";
        }
    }

    //下節內容寫這...
}

2.02、定義鏈表的屬性值

/**
 * 單向鏈表實作代碼
 */
public class SinglyLinkedList<E> {
    //省略以上代碼...

    private Node head;  //代表鏈表頭部
    private Node last;  //代表鏈表尾部
    private int size;   //代表鏈表長度

    //下節內容寫這...
}

2.03、初始化各個屬性值

/**
 * 單向鏈表實作代碼
 */
public class SinglyLinkedList<E> {
    //省略以上代碼...

    public SinglyLinkedList() {
        //頭結點用于其他結點的連接,頭結點的下標我們定義為-1
        this.head = new Node(null);
        //尾結點初始默認指向頭結點,這樣我們在添加的時候方便
        this.last = head;
        this.size = 0;
    }

    //下節內容寫這...
}

2.04、獲取鏈表當前大小

//獲取鏈表當前大小
public int size() {
    return size;
}

2.05、判斷鏈表是否為空

//判斷鏈表是否為空
public boolean isEmpty() {
    return size == 0;
}

2.06、檢查下標是否合法

//檢查下標是否合法
public void checkIndex(int index) {
    if (index < 0 || (size - 1) < index) {
        throw new IndexOutOfBoundsException("鏈表下標越界例外,請檢查鏈表的下標!");
    }
}

2.07、連接鏈表兩個結點

//連接鏈表兩個結點
public void connectNode(Node prevNode, Node nextNode) {
    if (prevNode != null) {
        prevNode.next = nextNode;
    }
}

2.08、釋放鏈表指定結點

//釋放鏈表指定結點
public void releaseNode(Node node) {
    if (node != null) {
        node.data = null;
        node.next = null;
    }
}

2.09、回傳鏈表最后結點

//回傳鏈表最后結點
public Node getLast() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return null;
    }
    //回傳鏈表最后結點
    return last;
}

2.10、回傳鏈表首個結點

//回傳鏈表首個結點
public Node getFirst() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return null;
    }
    //回傳鏈表首個結點
    return head.next;
}

2.11、獲取指定位置結點

//獲取指定位置結點
public Node getIndex(int index) {
    //檢查下標是否合法
    checkIndex(index);
    //獲取指定位置結點
    Node curNode = head;
    for (int i = 0; i < (index + 1); i++) {
        curNode = curNode.next;
    }
    //回傳指定位置結點
    return curNode;
}

2.12、獲取位置之前結點

//獲取位置之前結點
private Node getIndexPre(int index) {
    //檢查下標是否合法
    checkIndex(index);
    //獲取位置之前結點
    Node preNode = head;
    for (int i = 0; i < index; i++) {
        preNode = preNode.next;
    }
    //回傳位置之前結點
    return preNode;
}

2.13、鏈表尾后添加資料

默認為空的時候,頭指標head和尾指標last均指向頭結點,

添加資料的時候,我們直接向last指向結點后追加結點即可,


方法實作:

//鏈表尾后添加資料(需要考慮last指向問題)
public void addLast(E e) {
    //獲取舊的尾結點
    Node oldLast = last;
    //創建新的尾結點
    Node newLast = new Node(e);
    //舊新兩結點連接
    connectNode(oldLast, newLast);
    //修改last指向
    last = newLast;
    //使鏈表長度加一
    size++;
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三}
Node{data=李四}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三}
鏈表的尾結點:Node{data=王五}

2.14、鏈表頭后添加資料

方法實作:

//鏈表頭后添加資料(不用考慮last指向問題)
public void addFirst(E e) {
    //判斷鏈表是否為空
    if (isEmpty()) {
        addLast(e);
    } else {
        //獲取首個結點
        Node firNode = head.next;
        //創建新的結點
        Node newNode = new Node(e);
        //三個結點連接
        connectNode(head, newNode);
        connectNode(newNode, firNode);
        //鏈表長度加一
        size++;
    }
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addFirst("張三");
        linkedList.addFirst("李四");
        linkedList.addFirst("王五");

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=王五}
Node{data=李四}
Node{data=張三}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=王五}
鏈表的尾結點:Node{data=張三}

2.15、指定位置添加資料

方法實作:

//指定位置添加資料(不用考慮last指向問題)
public void addIndex(int index, E e) {
    //獲取位置之前結點
    Node preNode = getIndexPre(index);
    //獲取指定位置結點
    Node curNode = preNode.next;
    //創建一個新的結點
    Node newNode = new Node(e);
    //開始三個結點連接
    connectNode(preNode, newNode);
    connectNode(newNode, curNode);
    //當前鏈表長度加一
    size++;
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.addIndex(0, "張三長輩");
        linkedList.addIndex(linkedList.size() - 1, "王五長輩");

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:5
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三長輩}
Node{data=張三}
Node{data=李四}
Node{data=王五長輩}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三長輩}
鏈表的尾結點:Node{data=王五}

2.16、洗掉指定位置結點

方法實作:

//洗掉指定位置結點(需要考慮last指向問題)
public void removeIndex(int index) {
    //獲取位置之前結點
    Node preNode = getIndexPre(index);
    //獲取指定位置結點
    Node curNode = preNode.next;
    //獲取位置之后結點
    Node nexNode = curNode.next;
    //修改last的指向
    if (curNode == last) {
        last = preNode;
    }
    //洗掉指定位置結點
    connectNode(preNode, nexNode);
    //釋放鏈表指定結點
    releaseNode(curNode);
    //當前鏈表長度減一
    size--;
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.removeIndex(0);
        linkedList.removeIndex(linkedList.size() - 1);

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:1
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=李四}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=李四}
鏈表的尾結點:Node{data=李四}

2.17、洗掉鏈表首個結點

方法實作:

//洗掉鏈表首個結點(不用考慮last指向問題)
public void removeFirst() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //洗掉鏈表首個結點
    removeIndex(0);
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.removeFirst();

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:2
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=李四}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=李四}
鏈表的尾結點:Node{data=王五}

2.18、洗掉鏈表最后結點

方法實作:

//洗掉鏈表最后結點(不用考慮last指向問題)
public void removeLast() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //洗掉鏈表最后結點
    removeIndex(size - 1);
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.removeLast();

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.getIndex(i));
        }
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:2
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三}
Node{data=李四}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三}
鏈表的尾結點:Node{data=李四}

2.19、輸出鏈表所有結點

方法實作:

//輸出鏈表所有結點(不用考慮last指向問題)
public void show() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //遍歷鏈表所有結點(除頭結點)
    Node curNode = head.next;
    while (curNode != null) {
        System.out.println(curNode);
        curNode = curNode.next;
    }
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        linkedList.show();
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三}
Node{data=李四}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三}
鏈表的尾結點:Node{data=王五}

2.20、反轉鏈表所有結點

方法實作:

//反轉鏈表所有結點(需要考慮last指向問題)
public void reverse() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //創建一個新頭結點
    Node newHead = new Node(null);
    //獲取鏈表的首結點
    Node oldFirst = head.next;
    //遍歷鏈表所有結點(除頭結點)
    Node newFirst;
    Node tmpNode;
    Node curNode = oldFirst;
    while (curNode != null) {
        //快取當前結點下個結點
        tmpNode = curNode.next;
        //獲取鏈表新頭首個結點
        newFirst = newHead.next;
        //開始三個結點進行關聯
        connectNode(newHead, curNode);
        connectNode(curNode, newFirst);
        //讓當前的結點往后移動
        curNode = tmpNode;
    }
    //老頭換新頭首結點
    head.next = newHead.next;
    //修改last的指向
    last = oldFirst;
    //釋放臨時新頭結點
    releaseNode(newHead);
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.reverse();

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        linkedList.show();
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=王五}
Node{data=李四}
Node{data=張三}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=王五}
鏈表的尾結點:Node{data=張三}

2.21、清空鏈表所有結點

方法實作:

//清空鏈表所有結點(需要考慮last指向問題)
public void clear() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //重置鏈表基本資訊
    head = null;
    last = null;
    size = 0;
    //遍歷鏈表所有結點(含頭結點)
    Node tmpNode;
    Node curNode = head;
    while (curNode != null) {
        //快取下個結點
        tmpNode = curNode.next;
        //釋放當前結點
        releaseNode(curNode);
        //移動下個結點
        curNode = tmpNode;
    }
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        linkedList.clear();

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        linkedList.show();
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

==========列印鏈表基本資訊:
鏈表結點個數:0
鏈表是否為空:true
==========列印鏈表所有結點:
==========列印鏈表首尾結點:
鏈表的頭結點:null
鏈表的尾結點:null

2.22、順序查找資料首次出現位置

方法實作:

//順序查找資料首次出現位置
public int indexOf(E e) {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return -1;
    }
    //判斷物件是否為空
    if (e == null) {
        return -1;
    }
    //獲取指定位置結點
    Node curNode = head;
    for (int i = -1; curNode != null; i++) {
        if (e.equals(curNode.data)) {
            return i;
        }
        curNode = curNode.next;
    }
    //沒有找到回傳負一
    return -1;
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        System.out.println(linkedList.indexOf("張三"));
        System.out.println(linkedList.indexOf("李四"));
        System.out.println(linkedList.indexOf("王五"));

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        linkedList.show();
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

0
1
2
==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三}
Node{data=李四}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三}
鏈表的尾結點:Node{data=王五}

2.23、逆序查找資料首次出現位置

方法實作:

//逆序查找資料首次出現位置
public int lastIndexOf(E e) {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return -1;
    }
    //判斷物件是否為空
    if (e == null) {
        return -1;
    }
    //反轉當前鏈表結點
    reverse();
    //獲取指定位置結點
    int index = indexOf(e);
    //反轉當前鏈表結點
    reverse();
    //回傳資料指定位置
    return index;
}

方法測驗:

public class SinglyLinkedListTest {
    public static void main(String[] args) {
        SinglyLinkedList<String> linkedList = new SinglyLinkedList<>();

        linkedList.addLast("張三");
        linkedList.addLast("李四");
        linkedList.addLast("王五");

        System.out.println(linkedList.lastIndexOf("張三"));
        System.out.println(linkedList.lastIndexOf("李四"));
        System.out.println(linkedList.lastIndexOf("王五"));

        System.out.println("==========列印鏈表基本資訊:");
        System.out.println("鏈表結點個數:" + linkedList.size());
        System.out.println("鏈表是否為空:" + linkedList.isEmpty());
        System.out.println("==========列印鏈表所有結點:");
        linkedList.show();
        System.out.println("==========列印鏈表首尾結點:");
        System.out.println("鏈表的頭結點:" + linkedList.getFirst());
        System.out.println("鏈表的尾結點:" + linkedList.getLast());
    }
}

運行結果:

2
1
0
==========列印鏈表基本資訊:
鏈表結點個數:3
鏈表是否為空:false
==========列印鏈表所有結點:
Node{data=張三}
Node{data=李四}
Node{data=王五}
==========列印鏈表首尾結點:
鏈表的頭結點:Node{data=張三}
鏈表的尾結點:Node{data=王五}

第三章 雙向鏈表介紹

雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個資料結點中都有兩個指標,分別指向直接后繼和直接前驅,所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和后繼結點,鏈表的頭結點的資料域不存盤資料,指向前驅結點的指標域值為null,指向后繼結點的指標域指向第一個真正存盤資料結點,

第四章 雙向鏈表實作

4.01、且看代碼如此多嬌

我們已經學過了單向鏈表的設計和實作了,雙向鏈表只需要在單向鏈表的基礎上添加三行代碼就能實作,你看神奇不神奇,

我們需要直接拷貝SinglyLinkedListDoublyLinkedList,然后拷貝SinglyLinkedListTestDoublyLinkedListTest,并修改相對應的構造方法名稱,

第一處:修改節點類,添加prev指標域,

//定義結點類
public class Node {
    Node prev;  //指向上個結點 +
    E data;     //代表結點資料
    Node next;  //指向下個結點

    public Node(E data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "Node{data=" + data + "}";
    }
}

第二處:修改連接處,添加指向上個結點,

//連接鏈表兩個結點
public void connectNode(Node prevNode, Node nextNode) {
    if (prevNode != null) {
        prevNode.next = nextNode;
    }
    if (nextNode != null) {         // +
        nextNode.prev = prevNode;   // +
    }                               // +
}

第三處:修改釋放處,添加釋放prev指標域,

//釋放鏈表指定結點
public void releaseNode(Node node) {
    if (node != null) {
        node.prev = null;   // +
        node.data = null;
        node.next = null;
    }
}

到此,雙向鏈表的設計和實作就學完了,但是既然存在雙向鏈表,那么肯定還有一些方法可以簡化,接下來,我們需要對一些特殊的方法進行優化,

4.02、獲取位置之前結點

//獲取位置之前結點
private Node getIndexPre(int index) {
    return getIndex(index).prev;
}

4.03、洗掉鏈表最后結點

//洗掉鏈表最后結點(需要考慮last指向問題)
public void removeLast() {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return;
    }
    //獲取鏈表最后節點
    Node lastNode = last;
    //洗掉鏈表最后結點
    Node prevNode = lastNode.prev;
    prevNode.next = null;
    //釋放最后節點指向
    releaseNode(lastNode);
    //修改last的指向
    last = prevNode;
    //讓鏈表的長度減一
    size--;
}

4.04、逆序查找資料首次出現位置

//逆序查找資料首次出現位置
public int lastIndexOf(E e) {
    //判斷鏈表是否為空
    if (isEmpty()) {
        return -1;
    }
    //判斷物件是否為空
    if (e == null) {
        return -1;
    }
    //獲取指定位置結點
    Node curNode = last;
    for (int i = 0; curNode != null; i++) {
        if (e.equals(curNode.data)) {
            return i;
        }
        curNode = curNode.prev;
    }
    //沒有找到回傳負一
    return -1;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/267169.html

標籤:其他

上一篇:開發者們看過來,8ms開發工具平臺給大家送福利了!只要你來,肯定有你感興趣的,3.6-3.10日,只要在8ms平臺上創建專案,就有機會白嫖彩屏開發板哦

下一篇:leetcode453. 最小操作次數使陣列元素相等(賊難的簡單題)

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more