☰
LeetCode每日一题精讲:滑动窗口与动态规划实战
2026/10/28 12:31:33 网站建设 项目流程

1. LeetCode 每日一题精讲(2026/2/2-2026/2/8)

最近在整理LeetCode刷题笔记时,发现很多朋友对每日一题的解题思路和代码实现存在困惑。作为刷过800+题的ACMer,我将用工程视角拆解接下来一周的每日一题,不仅给出答案,更会分享如何建立解题思维框架。以下是2月2日到2月8日的完整题解路线图:

1.1 本周题目概览

  • 2/2 滑动窗口最大值(Hard)
  • 2/3 二叉树锯齿形遍历(Medium)
  • 2/4 最小覆盖子串(Hard)
  • 2/5 合并K个升序链表(Hard)
  • 2/6 打家劫舍III(Medium)
  • 2/7 课程表II(Medium)
  • 2/8 编辑距离(Hard)

提示:建议按顺序刷题,部分题目会用到前几天的解题技巧

2. 每日题解与思维训练

2.1 2/2 滑动窗口最大值(#239)

问题重述

给定数组nums和窗口大小k,返回每个窗口中的最大值。

最优解法:单调队列
from collections import deque def maxSlidingWindow(nums, k): q = deque() res = [] for i, num in enumerate(nums): while q and nums[q[-1]] <= num: q.pop() q.append(i) if q[0] == i - k: q.popleft() if i >= k - 1: res.append(nums[q[0]]) return res

时间复杂度:O(n),每个元素最多入队出队一次

关键突破点
  1. 维护单调递减队列:队首始终是当前窗口最大值
  2. 过期元素处理:当队首索引超出窗口范围时弹出
  3. 结果收集时机:从第k-1个元素开始记录结果

常见错误:直接暴力求解会导致O(nk)时间复杂度,在n=1e5时必然超时

2.2 2/3 二叉树锯齿形遍历(#103)

层序遍历变种
def zigzagLevelOrder(root): if not root: return [] res = [] q = deque([root]) left_to_right = True while q: level_size = len(q) level = [] for _ in range(level_size): node = q.popleft() level.append(node.val) if node.left: q.append(node.left) if node.right: q.append(node.right) if not left_to_right: level = level[::-1] res.append(level) left_to_right = not left_to_right return res

技巧:使用双端队列时,注意Python中list反转的时间复杂度是O(n),对于大规模数据建议改用deque的appendleft

2.3 2/4 最小覆盖子串(#76)

滑动窗口进阶
def minWindow(s, t): from collections import defaultdict need = defaultdict(int) for c in t: need[c] += 1 need_cnt = len(t) res = (0, float('inf')) left = 0 for right, c in enumerate(s): if need[c] > 0: need_cnt -= 1 need[c] -= 1 if need_cnt == 0: while True: c = s[left] if need[c] == 0: break need[c] += 1 left += 1 if right - left < res[1] - res[0]: res = (left, right) need[s[left]] += 1 need_cnt += 1 left += 1 return '' if res[1] > len(s) else s[res[0]:res[1]+1]

优化点:

  1. 使用need_cnt避免频繁统计字典
  2. 收缩左边界时跳过无关字符
  3. 哈希表记录字符需求差值

2.4 2/5 合并K个升序链表(#23)

分治+归并
def mergeKLists(lists): def mergeTwo(l1, l2): dummy = ListNode() p = dummy while l1 and l2: if l1.val < l2.val: p.next = l1 l1 = l1.next else: p.next = l2 l2 = l2.next p = p.next p.next = l1 if l1 else l2 return dummy.next if not lists: return None n = len(lists) interval = 1 while interval < n: for i in range(0, n - interval, interval * 2): lists[i] = mergeTwo(lists[i], lists[i+interval]) interval *= 2 return lists[0]

复杂度分析:

  • 时间复杂度:O(Nlogk),k是链表数量
  • 空间复杂度:O(1)

对比:优先队列解法需要O(k)空间,分治解法更优

2.5 2/6 打家劫舍III(#337)

树形DP
def rob(root): def dfs(node): if not node: return (0, 0) left = dfs(node.left) right = dfs(node.right) rob = node.val + left[1] + right[1] not_rob = max(left) + max(right) return (rob, not_rob) return max(dfs(root))

状态定义:

  • dp[0]:选择当前节点的最大收益
  • dp[1]:不选当前节点的最大收益

2.6 2/7 课程表II(#210)

拓扑排序
def findOrder(numCourses, prerequisites): from collections import deque adj = [[] for _ in range(numCourses)] indegree = [0] * numCourses for dest, src in prerequisites: adj[src].append(dest) indegree[dest] += 1 q = deque([i for i in range(numCourses) if indegree[i] == 0]) res = [] while q: node = q.popleft() res.append(node) for neighbor in adj[node]: indegree[neighbor] -= 1 if indegree[neighbor] == 0: q.append(neighbor) return res if len(res) == numCourses else []

关键检查:最终结果长度必须等于课程总数,否则存在环

2.7 2/8 编辑距离(#72)

经典DP
def minDistance(word1, word2): m, n = len(word1), len(word2) dp = [[0]*(n+1) for _ in range(m+1)] for i in range(m+1): dp[i][0] = i for j in range(n+1): dp[0][j] = j for i in range(1, m+1): for j in range(1, n+1): if word1[i-1] == word2[j-1]: dp[i][j] = dp[i-1][j-1] else: dp[i][j] = 1 + min( dp[i-1][j], # 删除 dp[i][j-1], # 插入 dp[i-1][j-1] # 替换 ) return dp[m][n]

状态转移:

  • 字符相等:继承左上角值
  • 字符不等:取三种操作最小值+1

3. 刷题方法论

3.1 题目分类训练法

  1. 模式识别:将题目归类到以下模板

    • 滑动窗口(2题)
    • 树形DP(1题)
    • 拓扑排序(1题)
    • 二维DP(1题)
  2. 解题四步法:

    • 理解题意(画图举例)
    • 确定算法(时间复杂度估算)
    • 编写伪代码(边界处理)
    • 实现+测试(特殊用例)

3.2 调试技巧

# 二叉树可视化工具 def printTree(root): from collections import deque q = deque([root]) while q: level_size = len(q) level = [] for _ in range(level_size): node = q.popleft() level.append(str(node.val) if node else 'null') if node: q.append(node.left) q.append(node.right) print(' '.join(level))

3.3 高频考点统计

考点出现次数相关题目编号
动态规划372, 337, 23
滑动窗口276, 239
树/图遍历2103, 210

4. 常见问题解答

4.1 如何选择编程语言?

  • Python:适合快速验证思路(本题解使用)
  • C++:追求极致性能(竞赛首选)
  • Java:工程化实现(面试常见)

4.2 时间不够怎么刷?

  1. 按频度排序:先刷Hot 100
  2. 同类题目串联:如一天专攻DP
  3. 善用模板代码:如快速实现Dijkstra

4.3 遇到死胡同怎么办?

  1. 设置思考时限(30分钟)
  2. 查看提示(前两行题解)
  3. 记录错题本(标注卡壳点)

5. 学习资源推荐

  1. 可视化平台:

    • LeetCode官方动画题解
    • VisuAlgo算法可视化
  2. 进阶书籍:

    • 《算法导论》(理论基础)
    • 《编程珠玑》(实战技巧)
  3. 刷题计划:

    • 剑指Offer(56题)
    • 代码随想录(分类题库)

最后分享我的个人经验:每天固定2小时刷题时间,先用10分钟手写伪代码,再20分钟实现,最后30分钟对比最优解。坚持三个月后,解题速度能提升300%以上。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询