首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 手寫單向鏈表:從節點設計到完整 CRUD

     編輯:Java 相關知識

鏈表 vs 陣列

鏈表適合**頻繁頭部操作、未知資料量、不需隨機存取**的場景 (如 LRU Cache、檔案系統目錄)。

節點設計

private static class Node<E> {
    E data;
    Node<E> next;
    Node(E data) { this.data = data; }
}

完整 NodeList 實作

public class NodeList<E> {
    private Node<E> head;      // 虛擬頭節點 (哨兵),簡化邊界條件
    private Node<E> tail;      // 尾指針,支援 O(1) 尾插
    private int size;          // 節點數

    public NodeList() { head = new Node<>(null); }

    // ---- 尾插 O(1) ----
    public void add(E data) {
        Node<E> node = new Node<>(data);
        if (tail == null) { head.next = tail = node; }
        else { tail.next = node; tail = node; }
        size++;
    }

    // ---- 指定元素後插入 ----
    public boolean addAfter(E target, E data) {
        Node<E> cur = head.next;
        while (cur != null) {
            if (eq(cur.data, target)) {
                Node<E> node = new Node<>(data);
                node.next = cur.next;
                cur.next = node;
                if (cur == tail) tail = node;
                size++; return true;
            }
            cur = cur.next;
        }
        return false;
    }

    // ---- 獲取索引元素 ----
    public E get(int index) {
        checkIndex(index);
        Node<E> cur = head.next;
        for (int i = 0; i < index; i++) cur = cur.next;
        return cur.data;
    }

    // ---- 修改元素 ----
    public boolean set(E oldVal, E newVal) {
        Node<E> cur = head.next;
        while (cur != null) {
            if (eq(cur.data, oldVal)) { cur.data = newVal; return true; }
            cur = cur.next;
        }
        return false;
    }

    // ---- 刪除元素 (首次出現) ----
    public boolean remove(E data) {
        Node<E> prev = head;
        Node<E> cur = head.next;
        while (cur != null) {
            if (eq(cur.data, data)) {
                prev.next = cur.next;
                if (cur == tail) tail = prev == head ? null : prev;
                size--; return true;
            }
            prev = cur;
            cur = cur.next;
        }
        return false;
    }

    // ---- 查詢 ----
    public boolean contains(E data) {
        Node<E> cur = head.next;
        while (cur != null) { if (eq(cur.data, data)) return true; cur = cur.next; }
        return false;
    }

    public boolean isEmpty() { return size == 0; }
    public int size() { return size; }
    public void clear() { head.next = null; tail = null; size = 0; }

    // ---- 列印 ----
    public void print() {
        if (isEmpty()) { System.out.println("空鏈表"); return; }
        Node<E> cur = head.next;
        while (cur != null) { System.out.print(cur.data + " -> "); cur = cur.next; }
        System.out.println("null");
    }

    private void checkIndex(int index) {
        if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index);
    }
    private boolean eq(Object a, Object b) { return a == null ? b == null : a.equals(b); }
}

關鍵設計亮點

  1. 虛擬頭節點 (Sentinel):head 不存資料,head.next 指向真實首節點。刪除首節點、空表操作無需特判,程式碼極度簡潔。
  2. 尾指針 tail:尾插、指定元素後插入更新尾部皆 O(1)。
  3. 統一 eq() 比較:正確處理 null 元素比較,避免 NullPointerException。
  4. 刪除更新 tail:刪除尾節點時正確回退 tail 指針。

測試驗證

public static void main(String[] args) {
    NodeList list = new NodeList<>();
    list.add("A"); list.add("B"); list.add("C");
    list.print(); // A -> B -> C -> null

    list.addAfter("B", "B2");
    list.print(); // A -> B -> B2 -> C -> null

    list.set("B2", "B_NEW");
    list.print(); // A -> B -> B_NEW -> C -> null

    list.remove("B");
    list.print(); // A -> B_NEW -> C -> null

    System.out.println("Size: " + list.size()); // 3
    System.out.println("Contains C: " + list.contains("C")); // true
    System.out.println("Get(1): " + list.get(1)); // B_NEW
}

進階延伸

  • 🔄 雙向鏈表:節點增 prev,支援雙向遍歷、O(1) 刪除任意節點 (Java LinkedList 即雙向)。
  • 🔁 循環鏈表:tail.next = head,適用於輪詢調度、Josephus 問題。
  • ⚡ 無鎖並發鏈表:CAS 操作 next 指針,參考 ConcurrentLinkedQueue 實現。

手寫鏈表是理解指標/引用操作、遞歸與迴圈轉換的最佳練習,也是面試高頻考點。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
特性陣列/ArrayList單向鏈表
記憶體連續離散 (節點指向下一個)
隨機存取O(1)O(n)
頭部插入/刪除O(n) 搬移O(1)
中間插入/刪除O(n) 搬移O(n) 找到前驅後 O(1)
記憶體開銷低高 (每節點額外引用)