我用Python刷完了LeetCode HOT 100:这是我最常用的10个解题模板和5个易错点
我用Python刷完了LeetCode HOT 100这是我最常用的10个解题模板和5个易错点去年夏天我给自己定下了一个小目标用Python完整刷完LeetCode HOT 100题目。三个月后当我终于完成最后一道hard题时代码提交记录已经积累了超过200次尝试。这段经历让我深刻体会到算法刷题不是简单的记忆题解而是需要建立自己的解题框架库。今天我想分享那些真正高频出现、能解决80%问题的核心代码模板以及那些看似简单却让我反复栽跟头的坑。1. 高频解题模板从双指针到动态规划1.1 双指针的三种经典范式双指针是我在数组和字符串类题目中使用最频繁的技巧根据场景不同可以分为三种典型模式同向快慢指针常用于去重、删除元素def removeDuplicates(nums): slow 0 for fast in range(len(nums)): if nums[fast] ! nums[slow]: slow 1 nums[slow] nums[fast] return slow 1相向指针适用于有序数组的两数之和等问题def twoSum(numbers, target): left, right 0, len(numbers)-1 while left right: sum_ numbers[left] numbers[right] if sum_ target: return [left1, right1] elif sum_ target: left 1 else: right - 1滑动窗口解决子串/子数组问题的通用模板def slidingWindow(s, t): window defaultdict(int) need defaultdict(int) for c in t: need[c] 1 left right valid 0 while right len(s): c s[right] right 1 # 窗口内数据更新 while (window needs shrink): d s[left] left 1 # 窗口内数据更新 return result1.2 回溯算法的标准化改造回溯题最容易写出冗长的代码我总结出这个通用框架后解组合、排列类题目效率大幅提升def backtrack(路径, 选择列表): if 满足结束条件: 结果.append(路径.copy()) return for 选择 in 选择列表: if 选择不合法: continue 做选择 backtrack(路径, 选择列表) 撤销选择实际应用时根据题目特点填充以下关键点选择合法性判断如去重路径记录方式通常用list终止条件如达到目标长度1.3 动态规划的状态转移方程库经过大量练习我发现DP问题可以归纳为几类核心状态转移模式线性DP如打家劫舍、股票问题dp[i] max(dp[i-1], dp[i-2] nums[i])二维DP矩阵路径类问题dp[i][j] dp[i-1][j] dp[i][j-1]背包问题的通用解法框架dp [0] * (target 1) dp[0] 1 # 初始化 for num in nums: for i in range(target, num-1, -1): # 01背包倒序 dp[i] dp[i - num]2. Python特有的五个深坑2.1 可变对象作为默认参数这个坑让我在回溯题上浪费了整整两小时# 错误写法 def dfs(path[]): pass # 正确写法 def dfs(pathNone): path path or []Python的函数默认参数在定义时就被创建所有调用共享同一个列表对象。2.2 浅拷贝导致的意外修改当需要保存中间状态时直接赋值会导致引用传递result [] path [1,2,3] result.append(path) # 错误后续修改path会影响result中的值 result.append(path.copy()) # 正确2.3 整数除法与浮点精度Python 3中/是真除法//是地板除。在二分查找时mid (left right) // 2 # 避免溢出更安全的写法left (right - left) // 22.4 字典键的存在性检查判断key是否存在的三种方式对比if key in dict: # 最Pythonic if dict.get(key): # 可能误判None/False if dict[key]: # KeyError风险2.5 循环中修改迭代对象在遍历list时删除元素会导致意外跳过# 错误方式 for i in range(len(nums)): if nums[i] target: del nums[i] # 后续元素前移导致漏检 # 正确方式 i 0 while i len(nums): if nums[i] target: del nums[i] else: i 13. 效率优化从暴力解到AC的进阶路径3.1 时间复杂度优化对照表问题类型暴力解法优化方案典型例题两数之和O(n²) 双重循环O(n) 哈希表#1 Two Sum连续子数组最大和O(n²) 枚举O(n) Kadane算法#53 Maximum Subarray字符串匹配O(mn) 逐个比较O(n) KMP算法#28 Implement strStr()3.2 空间换时间的典型场景前缀和频繁查询区间和#560记忆化递归中存在重复计算#70 Climbing Stairs哈希缓存快速查找元素#3 Longest Substring# 前缀和应用示例 def subarraySum(nums, k): prefix {0: 1} res s 0 for num in nums: s num res prefix.get(s - k, 0) prefix[s] prefix.get(s, 0) 1 return res4. 调试技巧如何快速定位边界条件错误4.1 必须测试的边界case空输入[]、、None单元素/双元素情况极值最大/最小整数完全升序/降序排列所有元素相同的情况4.2 防御性编程检查清单循环终止条件是否包含等号数组访问前是否检查了索引有效性除法操作是否有除零保护递归是否有终止条件变量初始化是否覆盖所有分支经验当你的代码通过示例但提交失败时优先检查循环的边界索引和递归的终止条件5. 我的刷题工具箱实用代码片段5.1 数据结构速建# 带虚拟头的链表 dummy ListNode(-1) dummy.next head # 二叉树节点定义 class TreeNode: def __init__(self, val0, leftNone, rightNone): self.val val self.left left self.right right5.2 常用工具函数# 快速反转字符串 s[::-1] # 统计元素频率 from collections import Counter count Counter(nums) # 堆操作 import heapq heapq.heapify(arr) val heapq.heappop(arr)刷完HOT 100的最大收获不是记住了多少题解而是建立了遇到新问题时快速拆解的能力。现在回头看最初花三小时都解不出来的medium题往往能在二十分钟内找到优化方向。这大概就是所谓的题感吧——知道该用什么工具也清楚哪里可能有坑。