點(diǎn)與快慢指針實(shí)戰(zhàn)套路)
我前一陣密集面了幾家公司發(fā)現(xiàn)一個(gè)挺有意思的現(xiàn)象不管面試官是什么級(jí)別、什么方向鏈表題幾乎成了“標(biāo)配開場(chǎng)”。尤其是準(zhǔn)備過牛客面試熱題101里鏈表系列之后很多題你甚至不用等面試官開口心里已經(jīng)能預(yù)判他要考你哪個(gè)變體。這份題庫的妙處在于——它不是堆題而是把鏈表能考的點(diǎn)全給串起來了。這篇文章我就以“面試拷打技術(shù)總監(jiān)”的視角把鏈表系列從頭到尾拆一遍。不只是給答案而是把為什么這么寫、面試官想聽到什么、實(shí)踐里哪些坑最致命全講清楚。準(zhǔn)備校招社招、轉(zhuǎn)碼刷題的朋友這篇可以直接當(dāng)作鏈表模塊的復(fù)習(xí)提綱。1. 鏈表在算法面試中的位置它到底在考你什么很多刷題新人會(huì)低估鏈表。覺得不就是節(jié)點(diǎn) next 指針嘛比數(shù)組難不到哪去。實(shí)際上鏈表題是面試?yán)镄詢r(jià)比極高的一類題原因在于它同時(shí)考察了三件事。第一是代碼基本功。鏈表題沒有現(xiàn)成的庫函數(shù)能繞過去每一步都是裸的指針/引用操作寫不寫得干凈利落、會(huì)不會(huì)多寫空指針判斷、斷鏈之后還記不記得接回去基本上一眼就能看出來有沒有工程底子。第二是邊界條件意識(shí)。空鏈表、只有一個(gè)節(jié)點(diǎn)、兩個(gè)節(jié)點(diǎn)、頭尾刪除、環(huán)的存在這些邊界在鏈表題里是高頻考察點(diǎn)。面試官往往不會(huì)直接提醒你他會(huì)在你寫完代碼后追問一句“如果鏈表只有一個(gè)節(jié)點(diǎn)呢如果 k 等于鏈表長(zhǎng)度呢”答不答得上來直接決定了這題的評(píng)級(jí)。第三是抽象建模能力。鏈表題里有相當(dāng)一部分不是單純考遍歷比如快慢指針、鏈表相加、合并 K 個(gè)排序鏈表這些題本質(zhì)是在考你能不能把一個(gè)復(fù)雜問題拆成簡(jiǎn)單的子問題或者識(shí)別出某些題其實(shí)是雙指針/歸并模式的變體。我面試的時(shí)候有個(gè)體會(huì)面試官其實(shí)不太在意這道題你以前做沒做過他在意的是你有沒有形成一套穩(wěn)定的解題方法論。做準(zhǔn)備要做到底層邏輯層面而不是背題。1.1 為什么我建議把 101 里的鏈表題當(dāng)“模塊”刷剛開始刷題容易陷入一個(gè)誤區(qū)——按題號(hào)順序刷今天刷到鏈表就刷鏈表明天跳到二叉樹就接著做。這樣刷到后面會(huì)有一個(gè)嚴(yán)重的副作用知識(shí)點(diǎn)是散的面試時(shí)調(diào)用不出來。我的做法是把鏈表系列拆成幾個(gè)子模塊每個(gè)模塊對(duì)應(yīng)一種核心技法基礎(chǔ)操作與虛擬頭節(jié)點(diǎn)覆蓋刪除類、插入類題目反轉(zhuǎn)與穿針引線覆蓋從整體反轉(zhuǎn)、區(qū)間反轉(zhuǎn)到 K 個(gè)一組反轉(zhuǎn)快慢指針與環(huán)覆蓋判環(huán)、環(huán)入口、倒數(shù)節(jié)點(diǎn)、找中點(diǎn)排序與合并覆蓋合并有序鏈表、K 路歸并、鏈表排序綜合模擬覆蓋鏈表相加、重排鏈表這類需要邊分析邊處理的問題。每個(gè)子模塊集中刷 5 到 8 道題刷完你會(huì)立刻發(fā)現(xiàn)題目之間是互通的很多題的核心就一兩行代碼剩下全是邊界處理。1.2 面試官真正期待的能力模型曾經(jīng)有位面試官直接跟我說過一句話鏈表題寫到后面判斷一個(gè)人的水平不是看他能不能 AC而是看他遇到“刪除某個(gè)節(jié)點(diǎn)但只給你這個(gè)節(jié)點(diǎn)本身”這種問題時(shí)的第一反應(yīng)。這其實(shí)是在說你有沒有養(yǎng)成“拿不到前驅(qū)就創(chuàng)造前驅(qū)”的思維習(xí)慣。這就是為什么虛擬頭節(jié)點(diǎn)在鏈表題里這么重要。它本質(zhì)上是一個(gè)“樁子”幫你把特殊位置頭部變成普通位置代碼邏輯就統(tǒng)一了。這種在面對(duì)邊界場(chǎng)景時(shí)主動(dòng)加緩沖層的意識(shí)放到工程里其實(shí)就是防御式編程。所以每次答鏈表題我腦子里會(huì)過一遍這里有幾種邊界哪些邊界用統(tǒng)一的技巧能消化掉時(shí)間復(fù)雜度是否仍是最優(yōu)這三個(gè)問題答完基本就不用擔(dān)心面試官再刁難。2. 三個(gè)必會(huì)的鏈表底層技能堆出所有解法的地基鏈表題的花樣再多底層靠的還是那三板斧。把這三樣練到肌肉記憶剩下就是識(shí)別題型套框架。2.1 虛擬頭節(jié)點(diǎn)讓頭部刪除不再特殊先看一個(gè)最簡(jiǎn)單的例子刪除單鏈表中等于給定值的所有節(jié)點(diǎn)。如果不用虛擬頭節(jié)點(diǎn)你得先寫一個(gè) while 循環(huán)把開頭的連續(xù)命中節(jié)點(diǎn)刪掉再處理中間部分。但如果帶一個(gè) dummy 頭節(jié)點(diǎn)代碼就變成同一套邏輯不會(huì)有任何位置分支def removeElements(head, val): dummy ListNode(0) dummy.next head pre, cur dummy, head while cur: if cur.val val: pre.next cur.next else: pre cur cur cur.next return dummy.next這個(gè)套路幾乎貫穿整個(gè) 101 鏈表模塊刪除鏈表的倒數(shù)第 N 個(gè)節(jié)點(diǎn)、刪除排序鏈表中的重復(fù)元素、移除鏈表元素都能用 dummy 把開頭位置的“特殊性”抹平。你只需要記住一句口訣不確定頭節(jié)點(diǎn)會(huì)不會(huì)變就加一個(gè)虛擬頭節(jié)點(diǎn)。2.2 三指針狀態(tài)保持移動(dòng)順序是唯一難點(diǎn)鏈表的很多操作本質(zhì)上是在維護(hù)一個(gè)“滑動(dòng)窗口”。反轉(zhuǎn)一個(gè)鏈表時(shí)維護(hù) pre、cur、next 三個(gè)指針判斷環(huán)時(shí)維護(hù) slow、fast 兩個(gè)指針刪除倒數(shù)第 N 個(gè)時(shí)維護(hù) first、second 兩個(gè)指針。指針本身的定義不難難的永遠(yuǎn)是指針移動(dòng)的先后順序。打個(gè)比方這就像你在挪動(dòng)一串手鏈上的珠子你要把某個(gè)珠子換到別的位置必須先松開前后兩顆珠子的連接然后把目標(biāo)珠子取出來再接到新位置。如果順序錯(cuò)了要么手鏈散架丟失鏈表后半段要么你自己都搞不清珠子是哪顆指針錯(cuò)亂。所以我每次寫鏈表題都會(huì)遵守一條鐵律修改任何節(jié)點(diǎn)的 next 之前先把它的下一個(gè)節(jié)點(diǎn)用臨時(shí)變量保存下來。我可以這樣告訴你所有鏈表題報(bào)錯(cuò)“運(yùn)行超時(shí)”或“訪問了空指針”八成都是斷鏈之后找不回來了。2.3 畫圖是列公式不是浪費(fèi)時(shí)間面試或者自己刷題時(shí)遇到稍微繞一點(diǎn)的題我建議直接在白板上畫鏈表節(jié)點(diǎn)之間的指向變化。不要只用腦推畫出來之后代碼就是照著圖寫的。這個(gè)能力是可以刻意訓(xùn)練的。我刷 101 鏈表系列的時(shí)候每道題都要求自己只對(duì)著圖寫代碼。一開始畫得很慢后來發(fā)現(xiàn)這套視覺化的工作流優(yōu)勢(shì)很大——腦子不需要同時(shí) hold 住“現(xiàn)在的狀態(tài)”和“下一步要做什么”畫完圖代碼自然就出來了。面試時(shí)還有個(gè)小技巧如果你需要在白板上畫圖面試官不但不會(huì)不耐煩反而會(huì)認(rèn)為你的解題方式可驗(yàn)證、可溝通屬于加分項(xiàng)。3. 反轉(zhuǎn)系列從迭代到遞歸把“穿針引線”想明白反轉(zhuǎn)鏈表是 101 鏈表系列里出現(xiàn)率肉眼可見高的題型。光一個(gè)整體反轉(zhuǎn)就有迭代、遞歸兩種寫法再往上加區(qū)間、按組翻轉(zhuǎn)、兩兩交換難度也會(huì)隨之上升。但核心只有一條主線段內(nèi)反轉(zhuǎn)的本質(zhì)是逐個(gè)把節(jié)點(diǎn)挪到頭部。3.1 整體反轉(zhuǎn)的迭代與遞歸實(shí)現(xiàn)暴力解法就不提了看迭代版。維護(hù) pre、cur 兩個(gè)指針不斷讓 cur.next 指向 pre然后整體右移def reverseList(head): pre None cur head while cur: nxt cur.next # 保存后繼防止斷鏈 cur.next pre # 反轉(zhuǎn)指向 pre cur cur nxt return pre遞歸版會(huì)稍微燒腦一點(diǎn)。核心思路是把“反轉(zhuǎn)整個(gè)鏈表”轉(zhuǎn)化為“反轉(zhuǎn) head.next 這個(gè)子鏈表再把 head 接到末尾”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面試時(shí)我更推薦先說迭代版因?yàn)楹媒忉?、不?huì)棧溢出。如果面試官追問“能不能用遞歸”再補(bǔ)充遞歸版本并說明遞歸會(huì)把大量棧幀壓棧鏈表太長(zhǎng)有風(fēng)險(xiǎn)。3.2 區(qū)間反轉(zhuǎn)與 K 個(gè)一組反轉(zhuǎn)的通用框架區(qū)間反轉(zhuǎn)反轉(zhuǎn)從 left 到 right 的部分比較通用的寫法是先找到 left 的前驅(qū)節(jié)點(diǎn)然后在這個(gè)區(qū)間內(nèi)重復(fù)執(zhí)行“頭插法”。所謂頭插法就是每次把當(dāng)前節(jié)點(diǎn)拎出來插入到區(qū)間頭部之后def reverseBetween(head, left, right): dummy ListNode(0) dummy.next head pre dummy for _ in range(left - 1): pre pre.next cur pre.next for _ in range(right - left): nxt cur.next cur.next nxt.next nxt.next pre.next pre.next nxt return dummy.nextK 個(gè)一組反轉(zhuǎn)只是把區(qū)間反轉(zhuǎn)重復(fù)多遍每輪找到第 K 個(gè)節(jié)點(diǎn)然后套頭插法。注意兩點(diǎn)一是 K 可能大于剩余長(zhǎng)度需要邊數(shù)邊處理二是反轉(zhuǎn)完一組后pre 要移動(dòng)到新的組頭。3.3 反轉(zhuǎn)類題目最容易踩的坑反轉(zhuǎn)類題目我在實(shí)際寫的時(shí)候踩過兩次坑。第一次是忘記把最后一段內(nèi)部鏈接翻轉(zhuǎn)完后末尾接上新組頭。其實(shí)這對(duì)應(yīng)代碼里 pre.next 的更新順序順序不對(duì)就會(huì)接錯(cuò)。第二次是“區(qū)間反轉(zhuǎn)”時(shí)把 cur 和 nxt 搞混導(dǎo)致鏈表成環(huán)。面試時(shí)這種成環(huán)錯(cuò)誤特別致命——代碼不會(huì)報(bào)錯(cuò)但跑測(cè)試會(huì)直接超時(shí)。我的調(diào)試經(jīng)驗(yàn)是遇到反轉(zhuǎn)類題就打印整個(gè)鏈表看看每個(gè)節(jié)點(diǎn)的 next 是不是和預(yù)期一致。比如打印 [1,2,3,4,5] 反轉(zhuǎn)后變成 [5,4,3,2,1]每個(gè)節(jié)點(diǎn)的數(shù)值和順序都能對(duì)上的話基本就穩(wěn)了。4. 快慢指針環(huán)、倒數(shù) K、交點(diǎn)三兄弟一次打盡快慢指針是鏈表模塊里最值得深挖的一類題。它表面是對(duì)兩個(gè)指針?biāo)俣鹊目刂茖?shí)際上背后是一套數(shù)學(xué)關(guān)系。把數(shù)學(xué)關(guān)系想清楚環(huán)形鏈表、環(huán)形鏈表入口、鏈表中倒數(shù)第 K 個(gè)節(jié)點(diǎn)、相交鏈表就全部變成同一類題的變體。4.1 判斷鏈表是否有環(huán)為什么快指針至少要快一步判斷環(huán)的經(jīng)典做法是快指針每次走兩步慢指針每次走一步。如果存在環(huán)兩指針必然相遇。有人會(huì)問為什么快指針不走三步、四步因?yàn)椴椒笥酗L(fēng)險(xiǎn)比如鏈表很長(zhǎng)但環(huán)很小快指針可能反復(fù)跳過慢指針?biāo)谖恢迷诃h(huán)內(nèi)跳格子導(dǎo)致遲遲不相遇。步幅差為 1 時(shí)最多走 O(n) 輪必然相遇是最穩(wěn)妥的。def hasCycle(head): slow fast head while fast and fast.next: slow slow.next # 慢指針走一步 fast fast.next.next # 快指針走兩步 if slow is fast: return True return False4.2 找環(huán)形入口位置記下一個(gè)等量關(guān)系進(jìn)階題是不僅要判斷有環(huán)還要找到環(huán)的入口。這題網(wǎng)上推導(dǎo)很多這里給一個(gè)便于記憶的結(jié)論。設(shè)鏈表頭到環(huán)入口的距離為 a入口到相遇點(diǎn)的距離為 b相遇點(diǎn)到下一次回到入口的距離為 c。當(dāng)快慢指針相遇時(shí)慢指針走了 ab快指針走了 ab n*(bc)。因?yàn)榭熘羔樞谐淌锹羔樀膬杀犊梢酝瞥?a c (n-1)*(bc)。這個(gè)式子翻譯成人話就是從相遇點(diǎn)開始走和一個(gè)從頭節(jié)點(diǎn)出發(fā)的新指針同步走兩者會(huì)在環(huán)入口相遇。代碼實(shí)現(xiàn)很直接def detectCycle(head): slow fast head while fast and fast.next: slow slow.next fast fast.next.next if slow is fast: ptr head while ptr is not slow: ptr ptr.next slow slow.next return ptr return None當(dāng)初刷到這里的時(shí)候我還特意畫了一堆圖去驗(yàn)證結(jié)論。面試的時(shí)候如果有人追問“為什么是走兩步而不是走一步”能把上面的等量關(guān)系講清楚會(huì)顯得你確實(shí)吃透了原理而不是背了模板。4.3 倒數(shù)第 K 個(gè)節(jié)點(diǎn)與相交鏈表找鏈表的倒數(shù)第 K 個(gè)節(jié)點(diǎn)用一個(gè)快指針先走 K 步然后快慢同步走快指針到底時(shí)慢指針正好指向倒數(shù)第 Kdef getKthFromEnd(head, k): fast slow head for _ in range(k): if not fast: return None fast fast.next while fast: fast fast.next slow slow.next return slow相交鏈表題的思路稍微需要變通一下讓兩個(gè)指針分別從 A、B 頭出發(fā)走到結(jié)尾后切換到對(duì)方鏈表繼續(xù)走。如果兩個(gè)鏈表有交點(diǎn)兩個(gè)指針必定會(huì)在交點(diǎn)相遇如果沒有交點(diǎn)它們會(huì)同時(shí)走到 None。這個(gè)思路本質(zhì)是讓兩個(gè)指針走過相同長(zhǎng)度。這幾個(gè)題掌握之后快慢指針類題的規(guī)律就清楚了先想清楚誰先走、速度差多少再想清楚相遇之后要利用哪個(gè)等量關(guān)系。5. 刪除、排序與合并高頻中等題的重災(zāi)區(qū)如果說反轉(zhuǎn)和快慢指針是熱身那刪除、排序、合并這幾類題才是回回卡人的地方。它們不像反轉(zhuǎn)那么“公式化”每題都需要結(jié)合具體場(chǎng)景設(shè)計(jì)細(xì)節(jié)非??简?yàn)邊界處理。5.1 刪除鏈表的倒數(shù)第 N 個(gè)節(jié)點(diǎn)虛擬頭節(jié)點(diǎn)加雙指針的標(biāo)準(zhǔn)配合這題是快慢指針和 dummy 節(jié)點(diǎn)的組合應(yīng)用。先讓 first 走 N 步然后 first 和 second 同步走first 到尾時(shí) second 正好指向待刪節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)。因?yàn)樯婕皠h除頭部的情況直接給鏈表加一個(gè)虛擬頭節(jié)點(diǎn)最穩(wěn)妥def removeNthFromEnd(head, n): dummy ListNode(0) dummy.next head first head for _ in range(n): first first.next second dummy while first: first first.next second second.next second.next second.next.next return dummy.next注意一個(gè)細(xì)節(jié)這里 second 初始化為 dummy而不是 head。這樣能保證 first 到底時(shí)second 指向“待刪節(jié)點(diǎn)”的前驅(qū)。如果把 second 初始化為 head遇到刪除頭節(jié)點(diǎn)的情況就會(huì)漏掉該有的處理。5.2 合并兩個(gè)有序鏈表與合并 K 個(gè)排序鏈表兩個(gè)有序鏈表合并是歸并排序的雛形。迭代版用一個(gè)哨兵頭節(jié)點(diǎn)維護(hù)結(jié)果鏈表的尾部指針遞歸版更短但要注意遞歸深度def mergeTwoLists(l1, l2): dummy ListNode(0) tail dummy while l1 and l2: if l1.val l2.val: tail.next l1 l1 l1.next else: tail.next l2 l2 l2.next tail tail.next tail.next l1 or l2 return dummy.next合并 K 個(gè)排序鏈表常見思路有三種兩兩合并、優(yōu)先隊(duì)列逐步取最小、分治歸并。我推薦優(yōu)先隊(duì)列的實(shí)現(xiàn)代碼直觀且時(shí)間效率穩(wěn)定。面試時(shí)你可以先把兩種思路都說出來再選自己熟悉的那版寫這樣會(huì)讓面試官覺得你是在做方案選型而不是死記硬背。5.3 鏈表排序歸并排序的“找中點(diǎn) 合并”組合拳單鏈表排序不能像數(shù)組那樣隨便隨機(jī)訪問所以最好寫的排序算法是歸并排序。步驟就三步用快慢指針找鏈表中點(diǎn)遞歸排序左右兩段用上面的 mergeTwoLists 合并。中點(diǎn)注意用 fast 和 fast.next 同時(shí)判空否則會(huì)取不到正確的左半部分。寫完后跑一個(gè) 5 個(gè)節(jié)點(diǎn)的奇數(shù)長(zhǎng)度和一個(gè) 4 個(gè)節(jié)點(diǎn)的偶數(shù)長(zhǎng)度用例基本就能確認(rèn)沒問題。5.4 鏈表相加進(jìn)位處理的兩種方向鏈表相加有兩種變體正序相加和逆序相加。逆序數(shù)字低位在前比較好處理直接同時(shí)遍歷兩條鏈表維護(hù)進(jìn)位即可。但正序相加時(shí)數(shù)字的低位在鏈表尾部此時(shí)可以先把兩條鏈表反轉(zhuǎn)再按逆序相加最后把結(jié)果反轉(zhuǎn)回來。這個(gè)思路本身不難難的是你當(dāng)場(chǎng)能不能意識(shí)到“反轉(zhuǎn)一下就能把問題轉(zhuǎn)化成熟悉的形態(tài)”。所以平時(shí)刷題我養(yǎng)成了一個(gè)習(xí)慣遇到不順手的題目先想想能不能通過一次反轉(zhuǎn)、加虛擬頭、或者雙指針把它變成做過的題。這一招在面試當(dāng)場(chǎng)非常救命。6. 綜合題與易錯(cuò)點(diǎn)面試場(chǎng)上最怕的翻車現(xiàn)場(chǎng)刷完上面幾大模塊后基本能做到 101 鏈表系列里的題一眼有思路但離“面試不翻車”還有一段距離。我覺得最后一段必須單獨(dú)聊聊面試現(xiàn)場(chǎng)的真實(shí)情況以及怎么在實(shí)戰(zhàn)中穩(wěn)定發(fā)揮。6.1 成環(huán)死循環(huán)的典型場(chǎng)景與快速排查鏈表題最常見的嚴(yán)重錯(cuò)誤就是成環(huán)——不是因?yàn)殒湵肀旧碛协h(huán)而是你在反轉(zhuǎn)、刪除、重排的過程中把 next 指錯(cuò)了造成循環(huán)。這種錯(cuò)誤最迷惑的地方在于程序不報(bào)錯(cuò)但測(cè)試時(shí)會(huì)超時(shí)。我自己就有一次在“重排鏈表”這道題上把 pre.next 和 cur.next 接反了代碼跑起來直接死循環(huán)。當(dāng)時(shí)完全沒想到是邏輯問題還以為是編譯器卡了。排查方法很老套但有效不要直接盲試先打印整個(gè)鏈表每次操作后的狀態(tài)。用 Python 的話可以寫一個(gè)輔助打印函數(shù)每次修改完鏈表就打印一遍。鏈表的節(jié)點(diǎn)數(shù)一般不大打印成本完全能接受。6.2 遞歸深度和棧溢出不是所有鏈表題都適合遞歸鏈表題里很多經(jīng)典解法都有遞歸版本比如反轉(zhuǎn)鏈表、合并有序鏈表、歸并排序。但面試時(shí)要多考慮一層如果這是一個(gè)幾萬節(jié)點(diǎn)的超長(zhǎng)鏈表遞歸會(huì)怎樣遞歸版本會(huì)占用 O(n) 的調(diào)用棧。面試官如果問“如果鏈表非常長(zhǎng)你的解法還適用嗎”其實(shí)就是在考察你有沒有意識(shí)到棧溢出風(fēng)險(xiǎn)。我的習(xí)慣是先說清楚遞歸版是一種優(yōu)雅的寫法然后主動(dòng)補(bǔ)充迭代版作為短鏈表的穩(wěn)妥選擇。不光是鏈表任何遞歸題我都很認(rèn)可“先講清楚兩種方案的取舍”的表達(dá)方式。這是高級(jí)工程師跟初級(jí)開發(fā)拉開差距的地方。6.3 面試表達(dá)從“會(huì)寫代碼”到“講清楚方案”代碼寫對(duì)了并不代表面試分?jǐn)?shù)一定高。我面過的所有公司里那些給出高評(píng)價(jià)的候選人共同點(diǎn)是都能用很清晰的邏輯把自己的解題過程講出來而不是悶頭寫完讓面試官自己看。鏈表題我建議按這個(gè)順序組織表達(dá)先說出題型的核心模式比如“這題是快慢指針 虛擬頭節(jié)點(diǎn)的典型應(yīng)用”再分析關(guān)鍵邊界比如刪除頭部、鏈表長(zhǎng)度為 1、k 大于鏈表長(zhǎng)度然后寫代碼邊寫邊解釋每個(gè)關(guān)鍵賦值動(dòng)作的目的最后主動(dòng)分析時(shí)間復(fù)雜度和空間復(fù)雜度。這套表達(dá)框架其實(shí)也是在向面試官展示工程能力面對(duì)一個(gè)需求你懂得先梳理方案、再處理邊界、最后落地實(shí)現(xiàn)。6.4 我練完 101 鏈表系列后的做題感受最后說一點(diǎn)個(gè)人體會(huì)。剛刷鏈表題的時(shí)候我最容易陷入“對(duì)著答案抄一遍就覺得自己會(huì)了”的假狀態(tài)。后來我換了個(gè)策略每天只刷 3 道鏈表題但每道題必須做到三遍。第一遍不看代碼自己試著寫第二遍對(duì)答案分析差距第三遍在完全不看筆記的情況下重新實(shí)現(xiàn)并加上邊界測(cè)試用例。這種方法比較笨但堅(jiān)持下來之后鏈表題在面試?yán)锘静粫?huì)再讓我恐慌。而且練到后期你會(huì)發(fā)現(xiàn)自己真的能跟面試官聊出些門道來——他拋一個(gè)變體你就能大概猜到他想聽哪個(gè)思路。所謂“拷打技術(shù)總監(jiān)”其實(shí)就是把這道題的所有角度都提前想過一遍等他開口提問時(shí)你已經(jīng)有備而來。