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

劍指 Offer 面試題 5:從尾到頭打印鏈表——四種解法全解析

     編輯:Java 相關知識

經典鏈表題:輸入單鏈表頭節點,從尾到頭打印節點值。本文詳細講解四種解法——棧、遞歸、ArrayList 逆序、就地反轉鏈表,對比時空複雜度、優缺點與適用場景,附完整 Java 代碼與測試用例。

題目描述

輸入:1 → 2 → 3 → 4 → null
輸出:4, 3, 2, 1,

本質是「後進先出」問題,天然適合棧或遞歸。

鏈表節點定義

static class ListNode {
    int val;
    ListNode next;
    ListNode(int v) { val = v; }
}

解法一:棧(顯式棧模擬後進先出)

public static void printWithStack(ListNode head) {
    Stack<Integer> stack = new Stack<>();
    while (head != null) {
        stack.push(head.val);
        head = head.next;
    }
    while (!stack.isEmpty()) {
        System.out.print(stack.pop() + ",");
    }
}
  • 時間:O(n) 兩次遍歷
  • 空間:O(n) 棧空間
  • 優點:不修改原鏈表,邏輯直觀,無棧溢出風險。

解法二:遞歸(系統調用棧隱式實現)

public static void printRecursive(ListNode head) {
    if (head == null) {
        System.out.println("空鏈表");
        return;
    }
    if (head.next != null) printRecursive(head.next);
    System.out.print(head.val + ",");
}
  • 時間:O(n)
  • 空間:O(n) 調用棧
  • 缺點:鏈表過長(> 10^4)易 StackOverflowError;不可中斷/恢復。

解法三:ArrayList 收集後逆序遍歷

public static void printWithArrayList(ListNode head) {
    if (head == null) { System.out.print("空鏈表"); return; }
    List<Integer> list = new ArrayList<>();
    while (head != null) {
        list.add(head.val);
        head = head.next;
    }
    for (int i = list.size() - 1; i >= 0; i--) {
        System.out.print(list.get(i) + ",");
    }
}
  • 時間:O(n)
  • 空間:O(n) ArrayList 底層陣列
  • 優點:隨機訪問靈活,可多次遍歷,無棧溢出風險。

解法四:就地反轉鏈表(修改結構,O(1) 額外空間)

public static void printByReverse(ListNode head) {
    ListNode reversed = reverseRecursive(head);
    while (reversed != null) {
        System.out.print(reversed.val + ",");
        reversed = reversed.next;
    }
}

// 遞歸反轉
private static ListNode reverseRecursive(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode newHead = reverseRecursive(head.next);
    head.next.next = head;
    head.next = null;
    return newHead;
}
  • 時間:O(n)
  • 空間:O(n) 遞歸棧(可改迭代反轉達到 O(1))
  • 缺點:破壞原鏈表結構,若後續仍需原順序需再反轉一次或備份。

迭代版反轉(真正 O(1) 空間)

private static ListNode reverseIterative(ListNode head) {
    ListNode prev = null, curr = head;
    while (curr != null) {
        ListNode next = curr.next;
        curr.next = prev;
        prev = curr;
        curr = next;
    }
    return prev;
}

四種解法綜合對比

解法時間額外空間修改原表棧溢出風險推薦場景
棧O(n)O(n)否無通用、面試首選
遞歸O(n)O(n) 棧否高短鏈表、代碼簡潔優先
ArrayListO(n)O(n)否無需多次逆序訪問
就地反轉O(n)O(1) 或 O(n)是遞歸版有空間極受限、允許修改

完整測試主程式

public static void main(String[] args) {
    // 建構 1→2→3→4
    ListNode n1 = new ListNode(1);
    ListNode n2 = new ListNode(2);
    ListNode n3 = new ListNode(3);
    ListNode n4 = new ListNode(4);
    n1.next = n2; n2.next = n3; n3.next = n4;

    System.out.print("棧:");        printWithStack(n1);        System.out.println();
    System.out.print("遞歸:");      printRecursive(n1);      System.out.println();
    System.out.print("ArrayList:"); printWithArrayList(n1); System.out.println();
    System.out.print("反轉:");      printByReverse(n1);      System.out.println();

    // 邊界測試
    printRecursive(null);       // 空鏈表
    printRecursive(new ListNode(5)); // 單節點
}

面試延伸問題

  1. 若不能修改鏈表且要求 O(1) 空間? → 不可能,需至少 O(n) 空間記錄順序(除非破壞結構)。
  2. 雙向鏈表如何優化? → 直接從尾節點往前遍歷,O(n) 時間 O(1) 空間。
  3. 海量數據流式處理? → 外部排序/分塊寫檔,或流式輸出到文件再反向讀取。

總結

從尾到頭打印鏈表考察對「後進先出」數據結構的靈活運用。面試建議:首推棧解法(不改結構、無溢出、邏輯清晰),次推 ArrayList;遞歸作為代碼簡潔展示;就地反轉作為「空間優化」進階方案並說明副作用。掌握四種解法權衡,即可應對所有變體追問。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved