用 Python 刷 LeetCode 本來應該是最輕鬆的選擇,語法簡潔、標準庫豐富。但有幾個細節讓我在某些題目上連錯好幾次,最後整理成這份紀錄,希望你不要踩同樣的坑。
這份筆記來自我的 GitHub repo chullin/leetcode,持續更新中。目前已完成 11 題,每題都嘗試用不同解法(Way1、Way2、Way3)來加深理解。
#1 pass by object reference(第 27 題)
這是我踩的第一個大坑。題目要求 in-place 修改陣列,我很直覺地寫:
nums = new_nums 只是把區域變數 nums 的指向改掉,原本傳進來的那個 list 完全沒有被動到。正確做法是用切片賦值,直接修改原本那塊記憶體的內容:
nums[:] 是在操作「這個 list 的內容」,而 nums = 是在操作「這個變數指向誰」。兩件完全不同的事。#2 整數除法與四捨五入(第 3643 題)
Python 3 的除法行為和很多語言不同,/ 一律回傳 float,而 // 才是整數除法:
四捨五入更要小心,Python 內建的 round() 使用「銀行家捨入法」(四捨六入五取偶),不是你想像中的普通四捨五入:
math.floor(x + 0.5) 比 round() 更可靠。#3 Python 的 Tuple Swap(第 3643 題)
這個是 Python 特有的語法糖,用來交換兩個變數超方便,在 Two Pointer 題型裡很常用:
(list[b], list[a]),再同時 unpack 到左邊。這個「先求值右邊」的特性保證了交換的正確性,不需要暫存變數。#4 Two Pointer 的思維(第 80、88 題)
做排序相關的題目時,in-place 操作幾乎都要用到 Two Pointer。關鍵是理解 slow / fast 兩個指針各自的職責:
第 80 題(Remove Duplicates II)的技巧是:因為每個元素最多出現兩次,陣列前兩個元素一定合法,所以 slow 從 index 2 開始,直接比較 nums[fast] 與 nums[slow - 2]。
#5 dict.get(key, default) 防 KeyError
在用 HashMap 計數時,第一次出現的 key 如果直接 count[char] += 1, 會因為 key 不存在而拋出 KeyError。常見的解法有三種:
Counter 最簡潔,適合「一次性計算整個字串/陣列的頻率」。
defaultdict 適合「邊遍歷邊更新,還要做分組」(如 Group Anagrams)。
dict.get 不需要 import,適合在受限環境或想明確控制邏輯時使用。
#6 range(nums) 忘記 len()
在寫 Two Pointer 的 for 迴圈時,我犯過一個很低級的錯——把 list 本身傳給 range():
TypeError,所以不難找到。 但它說明了一個思維習慣問題:在 Python 裡 range(len(arr)) 幾乎是固定搭配, 把它當成一個整體記住。#7 Counter 迭代順序 ≠ 頻率排序
做 #347 Top K Frequent Elements 時,我以為迭代 Counter 就能按頻率由高到低取得元素, 結果取到的是插入順序(Python 3.7+ dict 的預設行為),完全不是我要的結果。
Counter 的 most_common() 才是按頻率排序的正確方法。 如果不傳參數,會回傳全部元素按頻率排序的 list;傳入 k 只回傳前 k 個,效率更高。"刷題的目的不是背解法,而是把各種思維模式內化成直覺。"
目前進度
目前以 NeetCode 150 的順序為主,已完成:
* 每個藍點代表一種解法,目標是每題都找到至少兩種解法。