cswork / libraryOpen practice workspace

Articles

LeetCode 怎麼刷?用題型診斷、間隔複習與變體測試安排練習

LeetCode 怎麼刷?用題型先修關係選下一題,維護有到期日的複習佇列,再用負數視窗變體檢查方法前提。附原創程式與具體重做條件,不以題數保證錄取。

LeetCode 怎麼刷,關鍵不是每天累積幾題,而是知道下一題需要什麼能力、舊題何時該重做,以及換了條件後原本的方法還成不成立。把練習安排成「題型先修圖+有到期日的複習佇列+變體驗證」,比只留下 Accepted 紀錄,更容易看出下一個需要補的缺口。

這篇適合已經會使用題目頁、能寫基本程式,卻常常不知道接著練什麼的人。它不提供公司出題預測,也不把解題數當作錄取門檻。

先區分:不會用平台,還是不會安排練習?

如果卡點是函式模板、Run 與 Submit、讀題或看解答,先把第一題的完整操作做順。本文要處理的是另一層問題:你已經做過一些題,但練習順序不穩定,看過解答的題目持續增加,隔幾天又無法獨立寫出。

LeetCode 有公開的 Study Plan 入口,可以作為題目集合的起點;集合本身不會告訴你,每一題對你而言缺少哪個先修能力。LeetCode Study Plan 入口 因此,不必一遇到困難就換另一份清單。先把失敗定位成資料結構、推理、實作或驗證問題,再決定下一題。

若你需要面試模擬與較長期的整體安排,可另看完整的 coding interview practice 計畫。這篇只深入討論題型依賴、複習佇列與原創變體,不把整份面試準備計畫再重寫一次。

用先修圖找缺口,不把題型順序當成規定

先修能力是讓下一種方法可理解、可實作的基礎,不代表每個人都必須照同一條路走。下表是本文的編輯建議,不是 LeetCode 的官方學習順序。用「我能否證明這個條件」決定要不要前進,而不是只看題目標籤。

想練的方向先確認的能力可以觀察的理解門檻
雜湊計數陣列走訪、鍵與值、重複的意義不看解答,追蹤每個元素如何改變計數
前綴累積索引邊界、累積量與區間的關係能說明第0個前綴代表什麼,空區間怎麼算
雙指標、滑動視窗邊界更新、何時可丟棄候選答案能解釋左界為何只前進,以及所需前提
BFS佇列、圖的鄰居、拜訪狀態能說明何時標記已拜訪,避免重複加入
樹的遞迴函式回傳值、終止條件、呼叫堆疊明確區分節點結果與子樹結果
動態規劃狀態代表什麼、依賴順序能定義狀態、轉移、初始值,而非背表格

這不是一條單線:雜湊計數與前綴累積可以平行補,BFS 不需要先完成所有滑動視窗題。若你已能解釋樹的遞迴,也不用為了清單順序退回大量入門題。先修圖的用途,是避免把「還不理解狀態」誤判成「需要再背一個 DP 模板」。

題目標籤也不是完整演算法。LeetCode 的 Sliding Window Maximum 頁面同時列出滑動視窗、單調佇列等方向;Minimum Window Substring 則包含雜湊表與滑動視窗。兩題都出現視窗,不代表維護的資訊與收縮規則相同。Sliding Window Maximum、Minimum Window Substring

用一題做診斷,而不是一開始就跑完整清單

選一題代表你想練的方向,關掉解答做一次完整嘗試。留下三種不同證據:你在寫程式前提出的方法、實作後實際遇到的失敗輸入,以及你依靠了什麼協助。不要只記「Medium,不會」。

例如,知道要用字典卻一直混淆更新順序,是實作與狀態追蹤的缺口;不知道為什麼移動左界不會丟掉答案,是方法成立條件的缺口。前者可以先追蹤一個四元素輸入,後者需要重新推理候選範圍,不一定需要再做十道同標籤的題。

遇到卡點時,可以逐層取得協助:先查看相關概念,再看提示,最後才看完整解答。記錄哪一層幫你完成。看過完整解答後,即使當場重寫成功,也先標成「有協助完成」,讓複習佇列安排一次真正的獨立驗證。

時間也應記錄成觀察,而不是自我評分。你可能花較久建立正確不變條件,之後反而能處理變體。只追求第一次寫得快,容易把熟悉題目敘述誤當成可轉移的能力。

複習佇列要能回答「今天重做哪一題?」

複習佇列是一份有下一次驗證日期的待辦,不是越來越大的錯題收藏。每一列至少要有題型、成立前提、上次協助、下一次日期、驗證案例與結果。題目網址可以放在題名裡;詳細筆記另放,避免每次開表都重讀整篇解答。

題目/題型關鍵前提上次協助下次複習驗證案例狀態
原創預算視窗元素非負,預算非負看了收縮提示10/7全0、恰等於預算到期:獨立重寫
原創預算視窗變體允許負數尚未選出新方法10/8[4,-3,1]補推理,不背舊碼
樹的鏡像明確的空節點處理無協助完成10/10空樹、單節點驗證遞迴契約

上表是虛構的排程例子。明天、三天後、一週後可以當作初始間隔,但不是研究證明的最佳刷題週期。Karpicke 與 Roediger 的研究比較的是文字材料的提取練習;重複提取有助於該實驗中的記憶表現,但它沒有測試 LeetCode 解題,也不能替你確定固定天數。2010年提取與間隔研究

實際更新時,獨立完成且能說明前提,就把下次日期往後移;需要提示,安排較近的一次;再次失敗,保留原失敗原因,新增本次觀測,不把狀態改成一句「我太粗心」。若只是忘了語法,不必把整個題型降為不會;若前提講錯,即使程式碰巧通過,仍要補推理。

到期題太多時,控制佇列的流入

佇列更新也要有規則。假設10/7重做預算視窗,程式能跑,但你仍需要提示才想起「元素非負」;這次結果就不是無協助完成。保留10/7的紀錄,將下一次驗證設在較近日期,並把任務改成「先寫出允許縮左界的理由」。不要只把日期往後移,卻留下沒有完成的理解。

若10/8能獨立說明並寫出程式,再把下一次任務改成一個新案例,而非重跑原本四個數字。如此每一列都有具體的驗證條件。過期但已不符合目標的題目,也可以移出活躍佇列,保留歷史;複習清單不需要永遠保留所有做過的題。

不要讓新題持續進來、到期題永遠往後拖。假設今天可用的時間只有40分鐘,而三題都到期,可以先處理最會阻塞其他題型的一題,再做一個變體,最後更新剩餘日期。40分鐘只是安排示例,不是推薦給所有人的固定練習量。

排序時,先看會阻塞哪些能力,再看是否反覆需要同一種提示,最後才看距離上次練習多久。若五題都卡在區間邊界,不必五題逐一背解答;選一題重新寫清楚索引契約,再用另一題驗證是否轉移。

若到期清單一直增加,暫時少做新題不是退步。它代表你在處理已經投入時間、卻還沒有形成可獨立使用能力的部分。相反地,某個題型已連續獨立完成,就不需要每天重新做同一題來維持綠色紀錄。

原創實作:最長的不超預算連續區間

以下不是 LeetCode 題目原文,而是本文的獨立練習。給定非負整數序列與非負整數預算,回傳總和不超過預算的最長連續區間長度,允許長度0。輸入不是股票報酬或真實金額,只是整數。

[2,1,3,1]、預算4的答案是2,例如 [1,3]。元素都非負,所以擴大右界不會讓總和降低;超過預算時,縮小左界才可能恢復合法。這個單調性,是丟棄舊左界的理由。

def longest_window(values, budget):
    if type(budget) is not int or budget < 0:
        raise ValueError("invalid budget")
    if any(type(x) is not int or x < 0 for x in values):
        raise ValueError("values must be nonnegative integers")
    left = 0
    total = 0
    best = 0
    for right, value in enumerate(values):
        total += value
        while total > budget:
            total -= values[left]
            left += 1
        best = max(best, right - left + 1)
    return best

此版本的契約要求 values 是可重複走訪、可索引的有限序列。右界與左界各最多前進n次,包含前置驗證仍是O(n)時間;額外狀態是O(1)。這裡只回傳長度,不回傳區間,不需要處理同長度時選哪一段的規則。

用 [0,0,1]、預算0測試,答案應為2;空輸入回傳0;單一元素超過預算回傳0。這些案例分別檢查零值、空值與視窗清空,而不是三個幾乎相同的正常案例。

也可以先把 [2,1,3,1] 的狀態追蹤寫出來,再檢查程式有沒有遵守。索引從0開始,下表的總和是收縮完成後的值。

右界加入值收縮後左界收縮後總和當下合法長度
02021
11032
23142
31242

追蹤表不是另一份要背的答案。當結果不對時,比較第一個不同的狀態:是加值前後混淆、只縮了一次,還是長度少算一格?找到第一個偏離點,通常比最後才看 best 為何錯更容易定位。如果你能畫表卻寫不出更新順序,下次任務應是實作;如果表本身就錯,先修的是狀態推理。

變體測試:允許負數後,舊模板為什麼壞掉?

現在只改一個條件:元素可以是負數。對 [4,-3,1]、預算2,整段總和是2,答案應為3。如果看到第一個4超過預算就丟掉它,之後遇到−3時,已經無法重新取回從4開始的答案。

這不是把 while 改成 if 就能修好的語法問題,而是「之後擴大區間不會降低總和」的前提不成立。原函式刻意拒絕負數,避免默默給出錯誤答案。面試中應先指出新條件破壞了哪個論證,再討論替代方法。

可以先用小輸入的暴力枚舉當作參考答案:逐一固定起點與終點、累加並保留合法最長長度,時間O(n²)。若新的輸入上限允許,它可能就是足夠的解法;若不允許,再研究前綴和與能查詢合適索引的資料結構。不要在沒確認限制時,跳到一個自己無法證明的「最佳解」。

驗證原非負版本時,也可以用這個枚舉方法比較小型輸入。兩種實作使用不同策略,更有機會抓到同一模板裡的錯誤。這是本地程式正確性的有限檢查,不代表你已經通過任何公司測驗。

「看懂」要拆成三個可觀察結果

第一是重寫:隔一段時間,沒有解答也能寫出滿足原契約的程式。第二是說明:能講清楚為什麼移動邊界不會漏答案,以及時間、記憶體的來源。第三是轉移:換了前提,能辨認哪些部分仍可用、哪些需要重做。

三者不必同時達成。你可能已經能獨立實作,但複雜度說明還不穩;也可能能畫出方法,卻在索引上反覆出錯。把這些狀態分開記,下一次練習才會有明確目的。

變體也不必總是更難。只要求回傳最早起點而非長度,就能檢查你有沒有定義同長度的優先順序。把序列改成一次性的資料流,則會影響前置驗證與隨機存取。選擇一個真的改變契約的條件,比替換數字再跑一次更有價值。

用五個不同觀點,檢查先修能力

依照診斷結果挑選,不必為了完成表格而全部開做。

完整題名用來確認什麼
Explain Hash Map Collisions and Operation Complexity雜湊查詢的前提與複雜度
Product of Array Except Self Without Division前綴、後綴與額外空間的區別
Find minimum subarray length with k distinct integers視窗裡究竟要維護什麼狀態
Mirror a binary tree and analyze complexity遞迴回傳值與終止條件
Analyze time and space complexity看見重複工作與記憶體來源

開始前寫下本次要驗證的一句話,例如「我能解釋 distinct 計數何時增減」。做完後,留下證據、協助程度與下一次日期。這比把五個頁面加入收藏更接近可執行的練習安排。

下一次練習,先清楚一個前提

LeetCode 的清單可以幫你找到題目,自己的先修圖與複習佇列則用來決定順序。別用題目數掩蓋需要反覆提示的部分,也別因為某題失敗就否定所有已經建立的能力。

今天可以從需要維護不同元素數的區間問題開始:先說明狀態,再實作,再改一個條件。把驗證結果放回佇列,才算完成這一輪練習。

資料來源與延伸閱讀

資料確認:2026年10月6日。先修圖、複習排程與程式練習是本文的編輯建議;不是官方課程、經驗證的刷題最佳週期或錄取保證。