最近在整理上半年刷题记录时发现很多朋友在后台留言希望我能分享一些系统性的刷题方法和实战经验。确实LeetCode 作为技术面试的“金标准”其重要性不言而喻但面对海量题目如何高效规划、精准突破是每个开发者都会遇到的难题。本文将结合 2024 年上半年的刷题实践为你梳理一套从零基础到进阶的完整攻略涵盖高频考点、解题模板、时间规划以及避坑指南。无论你是准备暑期实习、秋招还是希望巩固算法基础都能从中找到可复用的路径。1. 背景与核心概念为什么 LeetCode 刷题如此重要在当前的软件开发求职市场中算法与数据结构能力是衡量候选人技术深度的核心标尺之一。LeetCode 平台汇聚了数千道编程题目覆盖了从数组、字符串到动态规划、图论等几乎所有计算机科学基础领域。它不仅仅是一个“刷题”网站更是一个模拟真实面试场景、锻炼问题分析与代码实现能力的训练场。对于开发者而言系统刷题能带来三大核心价值巩固基础知识很多题目是对经典数据结构如链表、树、堆和算法思想如分治、贪心、回溯的直接应用通过解题可以加深理解。提升编码熟练度与调试能力在时间限制下完成题目要求代码一次写对、边界清晰这极大地锻炼了编码的严谨性和自测Debug能力。熟悉面试套路与思维模式大厂面试题很多源于或改编自 LeetCode熟悉常见题目的变种和解题思路能在面试中更快地切入问题核心。然而盲目刷题往往事倍功半。常见的误区包括只追求题目数量而忽视总结遇到难题直接看答案缺乏独立思考没有分类规划知识点零散。本文将致力于解决这些问题提供一套可执行的系统化方案。2. 环境准备与版本说明工欲善其事必先利其器。一个高效的刷题环境能让你更专注于算法本身。2.1 编程语言选择选择一门你最为熟悉、且在面试中允许使用的语言。主流选择有Python语法简洁内置数据结构强大如列表、字典、集合在实现算法时往往代码量更少适合快速原型验证。是当前非常流行的刷题语言。Java强类型语言代码结构清晰企业级开发中使用广泛。需要注意其标准库的使用如PriorityQueue,HashMap。C执行效率高适合对性能有极致要求的题目但语法相对复杂。JavaScript前端开发者的首选需要注意运行环境差异。建议选定一门后在整个刷题周期内尽量保持统一以深化对该语言特性和标准库的掌握。2.2 集成开发环境IDE或编辑器本地 IDEPyCharm (Python), IntelliJ IDEA (Java), VS Code (全语言支持) 都是优秀的选择。它们提供代码补全、调试、版本控制集成等功能。在线平台LeetCode 官网自带的代码编辑器已足够好用支持运行和调试。对于想保存本地代码或进行版本管理的同学可以配合本地编辑器使用。2.3 辅助工具与习惯代码版本管理在本地为 LeetCode solutions 建立一个 Git 仓库按题目分类存放便于回顾和总结。笔记工具准备一个笔记本或使用 Notion、语雀等在线工具用于记录每道题的核心思路、易错点、时间/空间复杂度分析以及一题多解。调试技巧熟练掌握在 IDE 中设置断点、单步执行、查看变量状态的方法。对于在线平台善用print语句或console.log进行输出调试。版本说明本文的代码示例将主要使用Python 3进行演示因为其可读性强易于理解算法逻辑。其他语言的思路完全相通你可以自行转换。3. 核心知识点与解题模板拆解盲目刷题不如有效归类。掌握每一类题目的通用解题框架能让你在遇到新题时快速定位思路。3.1 数组与字符串双指针与滑动窗口这是最基础也是最常考的类型。双指针常用于有序数组的去重、找两数之和、合并有序数组等场景。# 示例移除有序数组中的重复项LeetCode 26 def removeDuplicates(nums): if not nums: return 0 slow 0 # 慢指针指向下一个唯一元素应该放置的位置 for fast in range(1, len(nums)): # 快指针遍历整个数组 if nums[fast] ! nums[slow]: slow 1 nums[slow] nums[fast] return slow 1 # 新数组的长度 # 核心思想快指针探索慢指针构建结果。滑动窗口用于解决子串、子数组的相关问题如“长度最小的子数组”、“无重复字符的最长子串”。# 示例长度最小的子数组LeetCode 209 def minSubArrayLen(target, nums): left 0 current_sum 0 min_length float(inf) for right in range(len(nums)): current_sum nums[right] # 扩大窗口 while current_sum target: # 满足条件时尝试收缩窗口 min_length min(min_length, right - left 1) current_sum - nums[left] left 1 # 收缩窗口 return min_length if min_length ! float(inf) else 0 # 核心思想用左右指针维护一个窗口通过移动右指针扩大窗口移动左指针收缩窗口在窗口满足条件时更新答案。3.2 链表虚拟头节点与快慢指针链表问题常涉及节点的增删改查技巧性较强。虚拟头节点Dummy Node可以简化对头节点的特殊处理尤其是在需要删除头节点时。# 示例删除链表的倒数第 N 个结点LeetCode 19 class ListNode: def __init__(self, val0, nextNone): self.val val self.next next def removeNthFromEnd(head, n): dummy ListNode(0, head) # 创建虚拟头节点指向原链表头 fast slow dummy # 快指针先走 n1 步 for _ in range(n 1): fast fast.next # 快慢指针同步前进直到快指针到达末尾 while fast: fast fast.next slow slow.next # 此时 slow 指向待删除节点的前一个节点 slow.next slow.next.next return dummy.next # 返回新的头节点快慢指针除了找倒数第N个节点还常用于判断链表是否有环、找环的入口。# 判断链表是否有环LeetCode 141 def hasCycle(head): if not head or not head.next: return False slow head fast head.next while slow ! fast: if not fast or not fast.next: # 快指针走到头了说明无环 return False slow slow.next fast fast.next.next return True # 快慢指针相遇说明有环3.3 二叉树递归与迭代遍历二叉树的题目几乎都建立在遍历的基础上。递归遍历前、中、后序代码简洁易于理解是基础。# 二叉树节点定义 class TreeNode: def __init__(self, val0, leftNone, rightNone): self.val val self.left left self.right right # 递归中序遍历 def inorderTraversal(root): result [] def dfs(node): if not node: return dfs(node.left) # 左 result.append(node.val) # 中 dfs(node.right) # 右 dfs(root) return result迭代遍历使用栈面试中常要求掌握以避免递归栈溢出的问题。# 迭代中序遍历使用栈模拟 def inorderTraversalIterative(root): result [] stack [] cur root while cur or stack: # 一路向左将节点入栈 while cur: stack.append(cur) cur cur.left # 弹出栈顶节点此时是最左边的节点 cur stack.pop() result.append(cur.val) # 转向右子树 cur cur.right return result3.4 动态规划DP识别状态与转移方程动态规划是难点但套路相对固定。核心是定义dp数组的含义并找出状态转移方程。解题步骤模板定义 dp 数组dp[i]或dp[i][j]代表什么确定初始状态dp[0]或dp[0][0]等边界情况的值。推导状态转移方程如何从已知状态推导出dp[i][j]确定遍历顺序。举例推导验证正确性。# 示例爬楼梯LeetCode 70 def climbStairs(n): if n 2: return n # 1. 定义dp数组dp[i]表示爬到第i阶楼梯的方法数 dp [0] * (n 1) # 2. 确定初始状态 dp[1] 1 dp[2] 2 # 3. 状态转移方程dp[i] dp[i-1] dp[i-2] # 4. 遍历顺序从3到n for i in range(3, n 1): dp[i] dp[i-1] dp[i-2] return dp[n] # 空间优化版滚动数组 def climbStairs_opt(n): if n 2: return n a, b 1, 2 # a代表dp[i-2], b代表dp[i-1] for i in range(3, n 1): a, b b, a b # 新的b就是dp[i] return b4. 2024年刷题实战规划与案例精讲有了基础知识我们需要一个科学的刷题计划。建议分为三个阶段周期约为3-4个月。4.1 第一阶段基础夯实约1个月目标掌握数据结构的基本操作和简单算法。每日任务5-10题Easy为主少量Medium。重点专题数组、字符串、链表、二叉树、栈、队列、哈希表。方法按专题刷每个专题刷15-20道经典题务必理解并默写核心代码。实战案例LeetCode 1. 两数之和这是哈希表应用的入门经典题。def twoSum(nums, target): :type nums: List[int] :type target: int :rtype: List[int] hash_map {} # 字典用于存储值到索引的映射 for i, num in enumerate(nums): complement target - num if complement in hash_map: # 检查补数是否已在字典中 return [hash_map[complement], i] # 找到返回索引 hash_map[num] i # 没找到将当前数字和索引存入字典 return [] # 题目保证有解这行不会执行 # 思路解析 # 暴力法是两层循环O(n^2)。利用哈希表我们可以在O(1)时间内查找“需要的另一个数”是否出现过。 # 遍历数组对于每个数nums[i]计算它需要的“另一半” complement target - nums[i]。 # 如果complement已经在哈希表中说明我们找到了配对。 # 否则将当前数nums[i]及其索引i存入哈希表供后续数字查找。 # 时间复杂度O(n)空间复杂度O(n)。4.2 第二阶段算法强化约1.5个月目标攻克中等难度题目掌握核心算法思想。每日任务3-5题Medium为主。重点专题深度优先搜索DFS、广度优先搜索BFS、回溯、贪心、动态规划、二分查找。方法继续按专题刷对于DP、回溯等难点可以配合图解和视频理解并总结自己的解题模板。实战案例LeetCode 200. 岛屿数量DFS/BFS这是二维矩阵DFS/BFS遍历的典型应用。# DFS 解法 def numIslands(grid): if not grid: return 0 rows, cols len(grid), len(grid[0]) count 0 def dfs(r, c): # 递归终止条件越界或不是陆地 if r 0 or r rows or c 0 or c cols or grid[r][c] ! 1: return # 将访问过的陆地标记为‘0’即“淹没” grid[r][c] 0 # 向四个方向深度搜索 dfs(r1, c) dfs(r-1, c) dfs(r, c1) dfs(r, c-1) for r in range(rows): for c in range(cols): if grid[r][c] 1: # 发现一块新陆地 count 1 dfs(r, c) # 用DFS“淹没”整个相连的岛屿 return count # 思路解析 # 核心是“沉没”思想。遍历整个网格当遇到一块陆地‘1’岛屿计数1。 # 然后通过DFS或BFS将这块陆地以及与其上下左右相连的所有陆地都标记为已访问例如改为‘0’。 # 这样后续遍历就不会再重复计数这些相连的陆地了。 # 时间复杂度 O(M*N)其中M和N是网格的行数和列数。4.3 第三阶段冲刺与模拟约1个月目标刷高频题、难题并进行限时模拟。每日任务2-3题Medium-Hard每周进行一次2小时的全套模拟面试。重点LeetCode Hot 100剑指 Offer以及近期周赛题目。方法不再按专题而是打乱顺序刷锻炼随机应变能力。严格计时模拟真实面试环境。实战案例LeetCode 73. 爱吃香蕉的狒狒二分查找这是一道典型的二分查找应用题关键在于将问题转化为“判断条件”。def minEatingSpeed(piles, h): :type piles: List[int] :type h: int :rtype: int # 狒狒吃香蕉的速度范围最小是1最大是香蕉堆中的最大值再大也没意义 left, right 1, max(piles) # 辅助函数计算以速度k吃完所有香蕉需要的小时数 def hours_needed(k): total_hours 0 for pile in piles: total_hours (pile k - 1) // k # 向上取整的巧妙写法 return total_hours # 二分查找最小的满足条件的速度k while left right: mid (left right) // 2 if hours_needed(mid) h: # 如果当前速度mid能在h小时内吃完尝试更小的速度 right mid else: # 当前速度太慢需要加快速度 left mid 1 return left # 此时left right即为最小速度 # 思路解析 # 暴力法是从1到max(piles)依次尝试但会超时。 # 注意到吃香蕉的速度k越大所需时间越少这是一个单调关系。 # 因此我们可以用二分查找来快速定位“能在h小时内吃完香蕉的最小速度k”。 # 二分查找的“判断条件”是计算以速度k吃完所有香蕉需要的时间total_hours。 # 如果total_hours h说明速度k可行但我们还想试试更小的速度收缩右边界。 # 如果total_hours h说明速度k太慢需要加大速度收缩左边界。 # 时间复杂度 O(N log M)其中N是堆数M是最大堆的香蕉数。5. 常见问题与排查思路在刷题过程中你一定会遇到各种“坑”。下面是一些高频问题及解决方案。问题现象常见原因解决思路与排查步骤提交后“超出时间限制”(TLE)1. 算法时间复杂度太高如用了嵌套循环。2. 递归深度过大未剪枝。3. 在循环中执行了低效操作如list.insert(0, ...)。1. 分析代码的时间复杂度尝试优化算法如用哈希表替代线性查找。2. 对于回溯/DFS检查是否可以进行剪枝Pruning。3. 检查数据结构的使用是否合理在头部频繁插入用deque。提交后“超出内存限制”(MLE)1. 使用了过大的辅助数组或缓存。2. 递归调用栈过深。3. 在DFS/BFS中未标记已访问节点导致重复入队/入栈。1. 尝试使用滚动数组优化DP的空间复杂度。2. 将递归改为迭代使用栈或队列。3. 确保图/树的遍历中节点一旦访问立即标记。答案错误(WA)1. 边界条件未考虑如空数组、单个元素。2. 索引越界。3. 状态转移方程推导错误。4. 整数溢出在某些语言中需注意。1.优先考虑边界用例输入为空、为1、为最大值/最小值时你的代码对吗2. 在循环中仔细检查索引的起始和结束位置。3. 用简单的测试用例手动模拟DP表格的填充过程。4. 使用打印语句或调试器跟踪关键变量的中间值。递归深度过大导致栈溢出Python默认递归深度约1000层对于深度很大的树或链表会出错。1. 尝试将算法改为迭代版本。2. 如果必须递归检查是否可以优化为尾递归但Python不优化尾递归。3. 使用sys.setrecursionlimit(limit)提高限制需谨慎。感觉思路对但代码总是写不对对算法细节理解不透彻或者代码实现能力有待提高。1.不要急着看答案给自己设定一个思考时间如30分钟尽力调试。2.动手画图在纸上画出数据结构的变化过程。3.写伪代码先理清步骤再转化为具体语言代码。4.对比优秀题解看完思路后关掉页面自己实现一遍。6. 最佳实践与工程建议将刷题视为一个工程项目来管理能极大提升效率和效果。6.1 代码规范与可读性命名规范变量名、函数名要见名知意。slow,fast比i,j更能体现双指针的意图dp比f更能表明是动态规划数组。注释关键步骤在复杂的逻辑处如状态转移、指针移动条件添加简短注释方便自己回顾和他人理解。函数单一职责一个函数最好只做一件事。例如把计算所需时间的逻辑抽成hours_needed(k)函数使主逻辑更清晰。6.2 总结与复盘体系一题多解对于经典题目尝试用不同方法解决。例如“两数之和”除了哈希表思考是否可以用排序双指针如果返回的是值而不是索引。归纳模板将同一类题目的解法抽象成模板。例如滑动窗口的代码框架、二叉树迭代遍历的栈操作顺序、回溯法的递归框架。错题本建立一个错题集记录第一次没做出来或做错的题目。定期如每周回顾分析当时卡壳的原因。复杂度分析养成习惯对每个解法都分析其时间复杂度和空间复杂度并思考是否有优化空间。6.3 模拟面试与时间管理限时练习平时练习就给自己计时Easy 15-20分钟Medium 25-30分钟Hard 40-50分钟培养时间感。白板编程偶尔尝试在纸上或纯文本编辑器里写代码锻炼在没有自动补全和语法高亮下的编码能力。口述思路在写代码前先尝试用语言清晰地描述解题步骤。这能锻炼你在面试中沟通的能力。定期回顾不要一味追求新题。每周留出时间快速重做之前做过的经典题和错题巩固记忆。6.4 心态与节奏保持节奏每天坚持刷1-3题比周末突击刷20题效果更好。不畏难题遇到Hard题即使想不出来认真思考半小时再看题解收获也比直接看答案大得多。善用资源LeetCode讨论区、官方题解、优质技术博客都是很好的学习资源但核心是内化成自己的知识。目标导向如果是为了面试后期应聚焦于目标公司的高频题和经典题。如果是为了竞赛则需要涉猎更广、钻研更深。刷题是一场马拉松而非冲刺。它考验的不仅是智力更是毅力、方法和习惯。通过2024年这半年的系统实践最大的感悟是刷题的本质是思维训练。数量的积累固然重要但更重要的是通过每一道题加深对某个数据结构或算法思想的理解并形成肌肉记忆。当你看到新题能迅速联想到归类和方法时你就已经成功了。从现在开始制定你的计划拿起笔和键盘一道题一道题地攻克你会在未来的某个时刻感谢今天开始行动的自己。