[面試攻略] 技術性考題:鏈表(Linked List)

更新於 發佈於 閱讀時間約 13 分鐘

鏈表是一種線性資料結構,由一系列節點組成,每個節點儲存一個值和指向下一個節點的參考。與陣列不同,鏈表的記憶體不連續,適合動態插入和刪除操作。在技術面試中,鏈表題目經常出現,因為它考驗你對資料結構和程式設計邏輯的掌握。本文將以反轉單向鏈表為例,介紹鏈表基礎、Python實現、時間與空間複雜度,並概述其他常見題目,提供面試答題技巧。


什麼是單向鏈表?

單向鏈表就像一列火車:每節車廂(節點)載著一個乘客(值),並連接到下一節車廂(下一個節點)。最後一節車廂沒有下一節,指向空(None)。

  • 節點結構: 值(val):儲存資料,例如數字或字串。 下一個節點(next):指向下一個節點的參考。
  • 頭節點:鏈表的起點,通過它可以遍歷整個鏈表。
  • 尾節點:最後一個節點,next 為 None。

Python節點定義

class ListNode:
def __init__(self, val=0, next=None):
self.val = val # 節點的值
self.next = next # 下一個節點的參考

核心題目:反轉單向鏈表

問題描述

給定一個單向鏈表,將其反轉並返回新的頭節點。例如:

輸入: 1 -> 2 -> 3 -> 4 -> 5 -> None
輸出: 5 -> 4 -> 3 -> 2 -> 1 -> None

要求:在原地反轉(不創建新鏈表,空間複雜度為 O(1))。

簡單比喻

想像一串珍珠項鍊,你需要把珍珠的順序反過來,但不能拆下珍珠再重串(不能用額外空間)。你只能改變每顆珍珠的「指向」,讓它連到前一顆而不是後一顆。

解法:迭代方法

反轉鏈表最簡單的方法是迭代,使用三個變數逐步改變節點的指向:

  1. 初始化: prev:前一個節點,初始為 None(因為反轉後原頭節點會指向 None)。 curr:當前節點,初始為頭節點。
  2. 遍歷: 儲存下一個節點(next = curr.next),避免斷鏈。 將當前節點指向前一個節點(curr.next = prev)。 移動 prev 和 curr(prev = curr, curr = next)。
  3. 結束: 當 curr 為 None 時,prev 是新頭節點。

程式碼範例

class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next

def reverseList(head):
prev = None # 前一個節點
curr = head # 當前節點
while curr:
next_node = curr.next # 儲存下一個節點
curr.next = prev # 反轉指向
prev = curr # 移動 prev
curr = next_node # 移動 curr
return prev # 新頭節點

# 輔助函數:創建鏈表
def createList(arr):
if not arr:
return None
head = ListNode(arr[0])
curr = head
for val in arr[1:]:
curr.next = ListNode(val)
curr = curr.next
return head

# 輔助函數:列印鏈表
def printList(head):
while head:
print(head.val, end=" -> ")
head = head.next
print("None")

# 測試
arr = [1, 2, 3, 4, 5]
head = createList(arr)
print("原始鏈表:", end=" ")
printList(head) # 輸出: 1 -> 2 -> 3 -> 4 -> 5 -> None
head = reverseList(head)
print("反轉後鏈表:", end=" ")
printList(head) # 輸出: 5 -> 4 -> 3 -> 2 -> 1 -> None

程式碼解釋

  • 節點類:ListNode 定義值和下一節點的參考。
  • 反轉邏輯:
    • 用 prev 和 curr 追蹤鏈表,逐步改變 next 指向。
    • next_node 確保不丟失下一個節點。
  • 輔助函數:
    • createList:從陣列創建鏈表,方便測試。
    • printList:列印鏈表內容,驗證結果。
  • Python優勢:無需手動管理記憶體(不像C/C++需要 malloc 和 free)。

時間與空間複雜度

  • 時間複雜度:O(n),其中 n 是鏈表長度,遍歷每個節點一次。
  • 空間複雜度:O(1),僅使用固定數量的變數(prev, curr, next_node)。

解法:遞迴方法

如果面試官要求遞迴解法,這裡提供一個簡單的遞迴實現:

解法:遞迴

  1. 遞迴到鏈表末端(最後一個節點成為新頭節點)。
  2. 從末端開始,將每個節點的 next 指向前一個節點。
  3. 返回新頭節點。

程式碼範例

def reverseList(head):
# 基本情況:空鏈表或單節點
if not head or not head.next:
return head
# 遞迴反轉剩餘部分
new_head = reverseList(head.next)
# 將當前節點連接到前一個節點
head.next.next = head
head.next = None
return new_head

時間與空間複雜度

  • 時間複雜度:O(n),遍歷每個節點。
  • 空間複雜度:O(n),因為遞迴棧的深度與鏈表長度成正比。

面試Tips

  • 迭代方法更常見,因為空間效率高(O(1))。
  • 如果使用遞迴,強調你理解兩種方法,並提到遞迴的空間開銷。

其他常見鏈表面試題目

面試中可能出現的鏈表題目多種多樣,以下是幾個高頻題目,幫助你全面準備:

  • 檢測鏈表是否有環(Linked List Cycle)
    • 問題:判斷鏈表是否有環(某節點的 next 指向先前節點)。
    • 解法:快慢指標(Floyd's Tortoise and Hare),快指標每次走兩步,慢指標走一步,若相遇則有環。
    • 複雜度:時間 O(n),空間 O(1)。
    • 範例:LeetCode #141。
  • 合併兩個有序鏈表(Merge Two Sorted Lists)
    • 問題:將兩個有序鏈表合併為一個有序鏈表。
    • 解法:逐一比較兩個鏈表的節點,選擇較小值連接到結果鏈表。
    • 複雜度:時間 O(n + m)(n 和 m 是兩鏈表長度),空間 O(1)(不計輸出)。
    • 範例:LeetCode #21。
  • 找到鏈表中間節點(Middle of the Linked List)
    • 問題:返回鏈表的中間節點(若長度為偶數,返回第二個中間節點)。
    • 解法:快慢指標,快指標走兩步,慢指標走一步,當快指標到達末尾,慢指標在中間。
    • 複雜度:時間 O(n),空間 O(1)。
    • 範例:LeetCode #876。
  • 刪除倒數第N個節點(Remove Nth Node From End)
    • 問題:給定 n,刪除鏈表倒數第 n 個節點。
    • 解法:快慢指標,快指標先走 n 步,然後一起走直到快指標到達末尾,慢指標指向待刪除節點的前一個節點。
    • 複雜度:時間 O(n),空間 O(1)。
    • 範例:LeetCode #19。
  • 合併K個有序鏈表(Merge K Sorted Lists)
    • 問題:合併 k 個有序鏈表。
    • 解法:使用最小堆(Priority Queue)或分治法,每次選最小節點。
    • 複雜度:時間 O(n * k * log k)(堆方法),空間 O(k)。
    • 範例:LeetCode #23。

面試應答策略

  • 理解題目:
    • 確認鏈表類型(單向、雙向、是否有環)。
    • 問清楚是否需要處理邊界情況(空鏈表、單節點)。
    • 確認空間複雜度要求(例如,反轉是否必須 O(1) 空間)。
  • 講解思路:
    • 用簡單比喻(火車、珍珠項鍊)讓面試官明白你的思考。
    • 畫圖展示節點和指向變化(例如,反轉時畫出 prev -> curr -> next)。
    • 提到時間和空間複雜度,解釋為什麼選擇某種方法(迭代 vs 遞迴)。
  • 程式碼實現:
    • 寫乾淨的程式碼,使用有意義的變數名(prev, curr, next_node)。
    • 註釋關鍵步驟,特別是改變 next 指向的部分。
    • 處理邊界情況(空鏈表:if not head: return None)。
  • 進階討論:
    • 如果問到優化,提到:
      • 快慢指標在檢測環或找中間節點中的應用。
      • 空間與時間的權衡(迭代節省空間,遞迴更簡潔但耗空間)。
    • 討論實際應用:
      • 鏈表用於動態資料(如瀏覽器歷史記錄)。
      • 與陣列的比較(鏈表插入快,隨機存取慢)。
    • 如果使用Python,提到垃圾回收自動處理記憶體,與C/C++的區別。
  • 常見追問:
    • 「如果鏈表有環怎麼辦?」
      • 回答:先用快慢指標檢測環,若無環再反轉;若有環,需找到環入口並斷開。
    • 「如何優化記憶體使用?」
      • 回答:迭代方法已是最優(O(1) 空間);遞迴可用尾遞迴優化(但Python不支援)。
    • 「Python與C++實現的區別?」
      • 回答:Python無需手動管理記憶體,節點是物件;C++需用指標並小心記憶體洩漏。
  • 模擬面試準備:
    • 在LeetCode練習鏈表題目(#206 Reverse Linked List, #141 Linked List Cycle, #21 Merge Two Sorted Lists)。
    • 手寫程式碼,模擬白板環境,練習邊寫邊講解。
    • 測試邊界情況(空鏈表、單節點、兩個節點)。
    • 熟悉Python的物件參考,確保不誤用賦值(如 curr = curr.next 不會複製節點)。

進階範例:檢測鏈表是否有環

以下是一個常見進階題目的Python實現,展示快慢指標的應用:

問題:檢測鏈表是否有環

def hasCycle(head):
if not head or not head.next:
return False
slow = head # 慢指標
fast = head # 快指標
while fast and fast.next:
slow = slow.next # 慢指標走一步
fast = fast.next.next # 快指標走兩步
if slow == fast: # 相遇表示有環
return True
return False

說明

  • 快慢指標:像兩個人跑步,快的人(fast)每次走兩步,慢的人(slow)走一步。如果有環,他們最終會相遇。
  • 邊界:檢查空鏈表或單節點,無環直接返回 False。
  • 複雜度:時間 O(n),空間 O(1)。

面試Tips

  • 畫圖展示快慢指標的移動,強調「相遇」的邏輯。
  • 如果問到「如何找到環的入口」,提到延伸解法:相遇後讓慢指標回到頭部,兩指標以相同速度走,相遇點即環入口。

結語

鏈表是面試中的必考資料結構,反轉鏈表是最基礎且經常出現的題目,掌握它能為其他題目打下基礎。使用Python實現鏈表題目時,重點在於理解節點參考的改變和邊界處理。練習時,專注於迭代解法(空間效率高),並熟悉快慢指標等技巧。模擬面試時,練習用簡單語言解釋(例如,火車比喻),並確保程式碼乾淨且正確。其他題目如檢測環或合併鏈表也很常見,建議在LeetCode上多練幾題。祝你面試順利!



留言
avatar-img
留言分享你的想法!
avatar-img
追極光的北極熊|軟體工程師的小天地
9會員
145內容數
歡迎來到我的部落格!這裡記錄了軟體工程師的日常生活點滴,並分享程式設計與演算法的實用教學。無論你是初學者還是有經驗的開發者,都能在這裡找到深入淺出的技術解析與實戰技巧。此外,我也會分享工作中的心路歷程與學習心得,讓你不僅學到技術,更能瞭解軟體開發的實際應用與挑戰。希望透過這個平台,能與你共同成長,激發對技術的熱情!
2025/05/22
i++(後置遞增)和++i(前置遞增)是C++中用來增加變數值的運算子,兩者在功能上相似但行為有細微差異。這個問題經常出現在技術面試中,因為它涉及運算子的返回值和執行效率。以下將以淺顯的方式解釋它們的差異、提供C++程式碼範例,並分享面試答題技巧。
2025/05/22
i++(後置遞增)和++i(前置遞增)是C++中用來增加變數值的運算子,兩者在功能上相似但行為有細微差異。這個問題經常出現在技術面試中,因為它涉及運算子的返回值和執行效率。以下將以淺顯的方式解釋它們的差異、提供C++程式碼範例,並分享面試答題技巧。
2025/05/16
函數指標(Function Pointer)是一個常見的技術面試題目,特別在C或C++相關的職位中,用來考驗你對程式設計中函數作為一等公民(first-class citizen)的理解,以及記憶體管理和程式控制流的掌握。
2025/05/16
函數指標(Function Pointer)是一個常見的技術面試題目,特別在C或C++相關的職位中,用來考驗你對程式設計中函數作為一等公民(first-class citizen)的理解,以及記憶體管理和程式控制流的掌握。
2025/05/10
在C++中,函數執行時的記憶體配置涉及程式如何在記憶體中儲存變數、參數和程式碼。這是面試常見題目,因為它直接關係到程式效率和資源管理。本文將介紹函數記憶體配置的基礎、C++程式碼範例、關鍵概念,以及面試答題技巧。
2025/05/10
在C++中,函數執行時的記憶體配置涉及程式如何在記憶體中儲存變數、參數和程式碼。這是面試常見題目,因為它直接關係到程式效率和資源管理。本文將介紹函數記憶體配置的基礎、C++程式碼範例、關鍵概念,以及面試答題技巧。
看更多
你可能也想看
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
TOMICA第一波推出吉伊卡哇聯名小車車的時候馬上就被搶購一空,一直很扼腕當時沒有趕緊入手。前陣子閒來無事逛蝦皮,突然發現幾家商場都又開始重新上架,價格也都回到正常水準,估計是官方又再補了一批貨,想都沒想就立刻下單! 同文也跟大家分享近期蝦皮購物紀錄、好用推薦、蝦皮分潤計畫的聯盟行銷!
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
每年4月、5月都是最多稅要繳的月份,當然大部份的人都是有機會繳到「綜合所得稅」,只是相當相當多人還不知道,原來繳給政府的稅!可以透過一些有活動的銀行信用卡或電子支付來繳,從繳費中賺一點點小確幸!就是賺個1%~2%大家也是很開心的,因為你們把沒回饋變成有回饋,就是用卡的最高境界 所得稅線上申報
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
題目敘述 Merge Nodes in Between Zeros 給定一個鏈結串列,合併非零區間的節點(以加總的方式合併),輸出合併後的鏈結串列。
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 輸入給定一個鏈結串列,整體看代表一個十進位的數字,各別看每個節點代表每個digit,分別從最高位~最低位個位數。 要求我們把原本的數字乘以二,並且以鏈結串列的形式返回答案。 原本的英文題目敘述
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
題目敘述 題目給定一個鏈結串列中的節點Node,要求我們從鏈結串列中刪除該節點。 題目保證該節點不是tail node。 題目要求我們in-place原位操作。 原本的英文題目敘述 測試範例 Example 1: Input: head = [4,5,1,9], node = 5
Thumbnail
給定一個Linked list鏈結串列的Head node, 請判斷這條Linked list是否存在環路(Cycle)? 如果有環路,回傳True。 如果沒有,回傳False。
Thumbnail
給定一個Linked list鏈結串列的Head node, 請判斷這條Linked list是否存在環路(Cycle)? 如果有環路,回傳True。 如果沒有,回傳False。
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
這篇文章,會帶著大家複習以前學過的遞回框架, 並且鏈結串列的概念與應用為核心, 貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個演算法框架。 遞回框架 尋找共通模式(common pattern),對應到演算法的General case 確立初始條件(initial conditio
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目會給定一個陣列nums和一個目標值goal。計算子陣列總和=goal的數目有多少。演算法包含前綴和和字典的技巧,時間複雜度為O(n),空間複雜度為O(n)。
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列的起始點,要求我們把其中區間總和為0的部分刪除掉。 例如 1→ 2 → -2 → 3 → 4 裡面有一段是2 → -2 區間總和為零,所以簡化刪除後變成 1→ 3 → 4 題目的原文敘述 測試範例 Example 1: Input: head
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給定一個鏈結串列 Linked List的頭部結點,要求我們根據索引的奇偶數重新排列。奇數索引的在前,偶數索引的在後。數的時候,從Head節點的索引=1開始數。 例如: 1 -> 2 -> 3 -> 4 -> 5 重新排列為 1 -> 3 -> 5 -> 2 -> 4
Thumbnail
題目敘述 題目會給我們一個鏈結串列的頭部結點Head node,要求我們計算鏈結串列中的Twin sum最大值是多少? 註: Twin Sum的定義就是頭尾結點相對位置相同的,互相配對加總在一起的值。 例如 給定串列= 1 -> 3 -> 2 -> 5 -> 100 -> 8 1, 8 一組
Thumbnail
題目敘述 題目會給我們一個鏈結串列的頭部結點Head node,要求我們計算鏈結串列中的Twin sum最大值是多少? 註: Twin Sum的定義就是頭尾結點相對位置相同的,互相配對加總在一起的值。 例如 給定串列= 1 -> 3 -> 2 -> 5 -> 100 -> 8 1, 8 一組
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
Thumbnail
題目敘述 題目會給定我們一條鏈結串列Linked list的起始節點,要求我們刪除Linked List正中央的節點。 註: 正中央的節點,題目定義為索引為floor( 串列長度 / 2 ) 的節點,索引從零(Head Node)出發開始數。 例如 1 -> 2 -> 3 -> 4 鏈結
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News