典題型與面試解題技巧)
1. 鏈表基礎(chǔ)與經(jīng)典題目價值鏈表作為數(shù)據(jù)結(jié)構(gòu)中的活化石在算法面試中始終占據(jù)著不可撼動的地位。不同于數(shù)組的連續(xù)存儲特性鏈表通過指針將零散的內(nèi)存塊串聯(lián)起來這種獨特的結(jié)構(gòu)使其在插入刪除操作上具有O(1)時間復雜度優(yōu)勢。我在技術(shù)面試中??吹胶蜻x人面對鏈表問題時陷入指針操作的泥潭——明明思路正確卻因為指針處理不當導致代碼崩潰。力扣平臺上鏈表相關(guān)題目超過200道其中約30道被標記為高頻面試題。根據(jù)我的刷題經(jīng)驗掌握以下10個經(jīng)典題型足以應(yīng)對90%的鏈表類面試單鏈表反轉(zhuǎn)力扣206鏈表中環(huán)的檢測力扣141合并兩個有序鏈表力扣21刪除鏈表的倒數(shù)第N個節(jié)點力扣19相交鏈表力扣160回文鏈表力扣234奇偶鏈表力扣328旋轉(zhuǎn)鏈表力扣61扁平化多級雙向鏈表力扣430LRU緩存機制力扣146提示鏈表問題的核心在于指針操作建議在紙上畫出節(jié)點和指針變化過程比單純腦補更不易出錯2. 核心題目解析與實現(xiàn)技巧2.1 單鏈表反轉(zhuǎn)力扣206這個Hello World級別的題目卻暗藏玄機。迭代法需要維護prev、curr、next三個指針def reverseList(head): prev None curr head while curr: next_node curr.next # 暫存后繼節(jié)點 curr.next prev # 指針反轉(zhuǎn) prev curr # 前驅(qū)后移 curr next_node # 當前后移 return prev遞歸解法更考驗對調(diào)用棧的理解def reverseList(head): if not head or not head.next: return head new_head reverseList(head.next) head.next.next head # 反轉(zhuǎn)指針 head.next None # 斷開原指針 return new_head常見坑點忘記處理原頭節(jié)點的next指針導致環(huán)狀鏈表迭代時丟失節(jié)點引用需先保存next節(jié)點遞歸深度過大導致棧溢出鏈表長度1000時考慮迭代2.2 鏈表中環(huán)的檢測力扣141快慢指針法是面試官最期待的解法def hasCycle(head): slow fast head while fast and fast.next: slow slow.next fast fast.next.next if slow fast: return True return False數(shù)學原理快指針每次比慢指針多走一步若有環(huán)必定相遇類似操場跑圈。時間復雜度O(n)空間復雜度O(1)優(yōu)于哈希表法的O(n)空間。進階問題找出環(huán)的入口點力扣142計算環(huán)的長度相遇后固定一個指針另一個繼續(xù)走直到再次相遇2.3 合并兩個有序鏈表力扣21遞歸和迭代兩種范式都需要掌握。迭代法常用dummy節(jié)點簡化邊界處理def mergeTwoLists(l1, l2): dummy ListNode(-1) curr dummy while l1 and l2: if l1.val l2.val: curr.next l1 l1 l1.next else: curr.next l2 l2 l2.next curr curr.next curr.next l1 if l1 else l2 return dummy.next注意實際面試中約30%的候選人會忘記處理剩余鏈表片段務(wù)必檢查l1/l2是否為None3. 高頻變種題型實戰(zhàn)3.1 刪除倒數(shù)第N個節(jié)點力扣19雙指針法的經(jīng)典應(yīng)用。讓fast指針先走n步然后同步移動直到fast到達末尾def removeNthFromEnd(head, n): dummy ListNode(0, head) fast slow dummy for _ in range(n): fast fast.next while fast.next: slow slow.next fast fast.next slow.next slow.next.next return dummy.next易錯點未考慮刪除頭節(jié)點的情況使用dummy節(jié)點解決fast指針移動次數(shù)錯誤應(yīng)移動n次而非n-1次邊界條件處理鏈表長度等于n時特殊處理3.2 相交鏈表力扣160這個題的精妙之處在于雙指針的路徑交換def getIntersectionNode(headA, headB): pA, pB headA, headB while pA ! pB: pA pA.next if pA else headB pB pB.next if pB else headA return pA原理兩個指針分別遍歷AB和BA長度相同必然在交點相遇或同時到達None。時間復雜度O(mn)空間O(1)。3.3 回文鏈表力扣234最優(yōu)解法結(jié)合了快慢指針和鏈表反轉(zhuǎn)def isPalindrome(head): # 找中點 slow fast head while fast and fast.next: slow slow.next fast fast.next.next # 反轉(zhuǎn)后半部分 prev None while slow: next_node slow.next slow.next prev prev slow slow next_node # 比較前后半段 left, right head, prev while right: if left.val ! right.val: return False left left.next right right.next return True注意事項快慢指針找中點時奇數(shù)長度slow停在正中偶數(shù)長度停在右中比較時只需比較到后半段結(jié)束避免奇數(shù)長度中間節(jié)點干擾如需保持原鏈表結(jié)構(gòu)需再次反轉(zhuǎn)恢復后半部分4. 工程實踐中的鏈表應(yīng)用4.1 LRU緩存實現(xiàn)力扣146雙向鏈表哈希表的經(jīng)典組合class DLinkedNode: def __init__(self, key0, value0): self.key key self.value value self.prev None self.next None class LRUCache: def __init__(self, capacity: int): self.cache {} self.capacity capacity self.head DLinkedNode() self.tail DLinkedNode() self.head.next self.tail self.tail.prev self.head def get(self, key: int) - int: if key not in self.cache: return -1 node self.cache[key] self._move_to_head(node) return node.value def put(self, key: int, value: int) - None: if key in self.cache: node self.cache[key] node.value value self._move_to_head(node) else: node DLinkedNode(key, value) self.cache[key] node self._add_to_head(node) if len(self.cache) self.capacity: removed self._remove_tail() del self.cache[removed.key] def _add_to_head(self, node): node.prev self.head node.next self.head.next self.head.next.prev node self.head.next node def _remove_node(self, node): node.prev.next node.next node.next.prev node.prev def _move_to_head(self, node): self._remove_node(node) self._add_to_head(node) def _remove_tail(self): node self.tail.prev self._remove_node(node) return node設(shè)計要點雙向鏈表維護訪問順序頭部最新尾部最舊哈希表實現(xiàn)O(1)訪問注意節(jié)點操作的順序先改新節(jié)點指針再改周圍節(jié)點邊界條件處理容量為1時的特殊情況4.2 多級鏈表扁平化力扣430深度優(yōu)先遍歷的典型應(yīng)用def flatten(head): if not head: return head dummy Node(0, None, head, None) stack [head] prev dummy while stack: curr stack.pop() prev.next curr curr.prev prev if curr.next: stack.append(curr.next) if curr.child: stack.append(curr.child) curr.child None prev curr dummy.next.prev None return dummy.next關(guān)鍵點使用棧實現(xiàn)DFS遍歷處理完child節(jié)點后要置空注意修正頭節(jié)點的prev指針時間復雜度O(n)空間復雜度O(n)最壞情況下5. 鏈表解題通用方法論經(jīng)過上百道鏈表題目的錘煉我總結(jié)出以下解題框架指針操作四要素當前節(jié)點(cur)前驅(qū)節(jié)點(prev)后繼節(jié)點(next)臨時節(jié)點(temp)邊界條件檢查清單空鏈表處理單節(jié)點鏈表頭節(jié)點/尾節(jié)點特殊處理指針越界檢查(cur.next操作前判空)調(diào)試技巧打印鏈表函數(shù)必備def print_list(head): while head: print(head.val, end - ) head head.next print(None)對長鏈表可打印前N個節(jié)點畫圖輔助理解指針變化性能優(yōu)化方向雙指針法替代多重循環(huán)哨兵節(jié)點(dummy)簡化邊界處理遞歸轉(zhuǎn)迭代避免棧溢出空間換時間如哈希表存儲節(jié)點面試應(yīng)答策略先陳述暴力解法再優(yōu)化明確時間/空間復雜度主動討論邊界條件手寫代碼時同步解釋指針變化最后分享一個真實案例在一次技術(shù)面試中候選人面對旋轉(zhuǎn)鏈表問題時先畫出k0, klen, klen三種情況的鏈表變化圖再編碼實現(xiàn)這種系統(tǒng)化的思考方式最終獲得了面試官的高度評價。鏈表問題的解決三分靠算法七分靠細心剩下的九十分全靠對指針操作的深刻理解。