在掌握这15种模式之前,力扣(LeetCode)对我来说一直很难。
攻克力扣的模式
在解决了1500多道力扣题目后,如果说我学到了什么,那就是:力扣考察的并非你做过题目的数量,而是你掌握了多少解题模式。学习这些模式能让你在更短时间内解决各类题目,还能帮你快速找到从未见过的题目的正确解法。
力扣解题模式
在本文中,我会带你了解我学到的15种最重要的解题模式,它们让我在力扣的刷题之旅轻松了许多。我会分享每种模式的适用场景、一道示例题目,并提供力扣练习题链接,以便你更好地学习这些模式。
- 前缀和
数组A:1 2 3 4 5 6
前缀和数组P:1 3 6 10 15 21
前缀和是指对数组进行预处理,创建一个新数组,新数组中索引i
处的元素代表原数组从起始位置到i
的元素之和。这样能高效地处理子数组的求和查询。当你需要对一个子数组进行多次求和查询,或者计算累积和时,可以使用这种模式。
- 示例题目:给定一个数组nums
,回答多个关于特定范围[i, j]
内元素之和的查询。
- 示例:
- 输入:nums = [1, 2, 3, 4, 5, 6]
,i = 1
,j = 3
- 输出:9
- 解释:预处理数组A
得到前缀和数组P = [1, 3, 6, 10, 15, 21]
。要计算索引i
和j
之间的和,使用公式P[j] - P[i - 1]
。
- 力扣题目:
- 区域和检索 - 数组不可变(力扣第303题)
- 连续数组(力扣第525题)
- 和为K的子数组(力扣第560题)
2. 双指针
双指针模式是使用两个指针遍历数组或列表,常用于查找满足特定条件的元素对。在处理有序数组或列表,且需要找到满足特定条件的元素对时,可使用这种模式。
- **示例题目**:在有序数组中找到两个数,使它们的和等于目标值。
- **示例**:
- 输入:`nums = [1, 2, 3, 4, 6]`,`target = 6`
- 输出:`[1, 3]`
- 解释:初始化两个指针,一个指向数组开头(`left`),一个指向数组末尾(`right`)。检查两个指针指向元素的和。如果和等于目标值,返回这两个元素的索引。如果和小于目标值,将左指针向右移动。如果和大于目标值,将右指针向左移动。
- **力扣题目**:
- 两数之和 II - 输入有序数组(力扣第167题)
- 三数之和(力扣第15题)
- 盛最多水的容器(力扣第11题)
-
滑动窗口
滑动窗口模式用于寻找满足特定条件的子数组或子字符串,通过维护一个元素窗口来优化时间复杂度。在处理涉及连续子数组或子字符串的问题时,可使用这种模式。
- 示例题目:找到大小为
k
的子数组的最大和。 - 示例:
- 输入:
nums = [2, 1, 5, 1, 3, 2]
,k = 3
- 输出:
9
- 解释:先计算前
k
个元素的和。每次将窗口滑动一个元素,减去移出窗口的元素,加上新进入窗口的元素。记录遇到的最大和。
- 输入:
- 力扣题目:
- 最大平均子数组 I(力扣第643题)
- 无重复字符的最长子串(力扣第3题)
- 最小覆盖子串(力扣第76题)
- 示例题目:找到大小为
-
快慢指针
快慢指针(又称龟兔赛跑算法)模式用于检测链表及其他类似结构中的环。
- 示例题目:检测链表是否有环。
- 解释:初始化两个指针,一个每次移动一步(慢指针),另一个每次移动两步(快指针)。如果存在环,快指针最终会与慢指针相遇。如果快指针到达链表末尾,则不存在环。
- 力扣题目:
- 环形链表(力扣第141题)
- 快乐数(力扣第202题)
- 寻找重复数(力扣第287题)
-
链表原地反转
链表原地反转模式是指在不使用额外空间的情况下反转链表的部分内容。当需要反转链表的部分区段时,可使用这种模式。
- 示例题目:反转链表中从位置
m
到n
的子链表。 - 示例:
- 输入:
head = [1, 2, 3, 4, 5]
,m = 2
,n = 4
- 输出:
[1, 4, 3, 2, 5]
- 解释:确定子链表的起始和结束位置。通过调整指针原地反转节点。
- 输入:
- 力扣题目:
- 反转链表(力扣第206题)
- 反转链表 II(力扣第92题)
- 两两交换链表中的节点(力扣第24题)
- 示例题目:反转链表中从位置
-
单调栈
单调栈模式使用栈来按特定顺序(递增或递减)维护元素序列。适用于需要查找下一个更大或更小元素的问题。
- 示例题目:找到数组中每个元素的下一个更大元素,如果不存在则输出 -1。
- 示例:
- 输入:
nums = [2, 1, 2, 4, 3]
- 输出:
[4, 2, 4, -1, -1]
- 解释:使用栈记录尚未找到下一个更大元素的元素。遍历数组,对于每个元素,弹出栈中元素,直到找到更大的元素。如果栈不为空,将栈顶元素的结果设为当前元素。将当前元素压入栈中。
- 输入:
- 力扣题目:
- 下一个更大元素 I(力扣第496题)
- 每日温度(力扣第739题)
- 柱状图中最大的矩形(力扣第84题)
-
前K个元素
前K个元素模式用于使用堆或排序,在数组或数据流中找到前k
个最大或最小的元素。
- 示例题目:在未排序数组中找到第
k
大的元素。 - 示例:
- 输入:
nums = [3, 2, 1, 5, 6, 4]
,k = 2
- 输出:
5
- 解释:使用大小为
k
的最小堆记录最大的k
个元素。遍历数组,将元素加入堆中。如果堆的大小超过k
,移除堆中最小的元素。堆顶元素即为第k
大的元素。
- 输入:
- 力扣题目:
- 数组中的第K个最大元素(力扣第215题)
- 前 K 个高频元素(力扣第347题)
- 找出和最小的K对数字(力扣第373题)
- 示例题目:在未排序数组中找到第
-
重叠区间
重叠区间模式用于合并或处理数组中的重叠区间。在按起始时间排序的区间数组中,如果两个区间[a, b]
和[c, d]
满足b >= c
(即第一个区间的结束时间大于或等于第二个区间的起始时间),则它们重叠。
- 示例题目:合并所有重叠区间。
- 示例:
- 输入:
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]]
- 输出:
[[1, 6], [8, 10], [15, 18]]
- 解释:按起始时间对区间进行排序。创建一个空列表
merged
来存储合并后的区间。遍历区间,检查其是否与merged
列表中的最后一个区间重叠。如果重叠,通过更新merged
列表中最后一个区间的结束时间来合并区间。如果不重叠,直接将当前区间添加到merged
列表中。
- 输入:
- 力扣题目:
- 合并区间(力扣第56题)
- 插入区间(力扣第57题)
- 无重叠区间(力扣第435题)
-
二分查找变形
二分查找变形模式是对二分查找的扩展,用于解决更广泛的问题,如在旋转排序数组中查找元素。在处理有序或旋转排序数组,且需要查找特定元素的问题时,可使用这种模式。
- 示例题目:在旋转排序数组中查找元素。
- 示例:
- 输入:
nums = [4, 5, 6, 7, 0, 1, 2]
,target = 0
- 输出:
4
- 解释:执行二分查找时,增加额外检查以确定数组的哪一半是有序的。然后检查目标元素是否在有序的那一半范围内。如果是,就在那一半中查找;否则,在另一半中查找。
- 输入:
- 力扣题目:
- 搜索旋转排序数组(力扣第33题)
- 寻找旋转排序数组中的最小值(力扣第153题)
- 搜索二维矩阵 II(力扣第240题)
-
二叉树遍历
二叉树遍历是指按特定顺序访问二叉树中的所有节点。
- 前序遍历:
root -> left -> right
- 中序遍历:
left -> root -> right
- 后序遍历:
left -> right -> root
- 示例题目:对二叉树进行中序遍历。
- 示例:
- 输入:
root = [1, null, 2, 3]
- 输出:
[1, 3, 2]
- 解释:中序遍历按左节点、根节点、右节点的顺序访问节点。可以使用递归或栈按此顺序遍历树。
- 输入:
- 力扣题目:
- 前序遍历 → 二叉树的所有路径(力扣第257题)
- 中序遍历 → 二叉搜索树中第K小的元素(力扣第230题)
- 后序遍历 → 二叉树中的最大路径和(力扣第124题)
- 前序遍历:
-
深度优先搜索(DFS)
深度优先搜索(DFS)是一种遍历技术,它会尽可能深入一条分支,然后再回溯。用于探索图或树中的所有路径或分支。
- 示例题目:找出二叉树中从根节点到叶子节点的所有路径。
- 示例:
- 输入:
root = [1, 2, 3, null, 5]
- 输出:
["1->2->5", "1->3"]
- 解释:使用递归或栈遍历从根节点到叶子节点的每条路径。在遍历过程中记录每条路径。
- 输入:
- 力扣题目:
- 克隆图(力扣第133题)
- 路径总和 II(力扣第113题)
- 课程表 II(力扣第210题)
-
广度优先搜索(BFS)
广度优先搜索(BFS)是一种遍历技术,它按层次依次探索树或图中的节点。用于在无权图中查找最短路径,或对树进行层序遍历。
- 示例题目:对二叉树进行层序遍历。
- 示例:
- 输入:
root = [3, 9, 20, null, null, 15, 7]
- 输出:
[[3], [9, 20], [15, 7]]
- 解释:使用队列记录每一层的节点。遍历每一层,并将当前节点的子节点加入队列。
- 输入:
- 力扣题目:
- 二叉树的层序遍历(力扣第102题)
- 腐烂的橘子(力扣第994题)
- 单词接龙(力扣第127题)
-
矩阵遍历
矩阵遍历是指使用不同技术(如深度优先搜索、广度优先搜索等)遍历矩阵中的元素。适用于涉及水平、垂直或对角遍历二维网格或矩阵的问题。
- 示例题目:对二维网格进行洪水填充。将与起始单元格相连的所有单元格颜色更改为新颜色。
- 示例:
- 输入:
image = [[1,1,1],[1,1,0],[1,0,1]]
,sr = 1
,sc = 1
,newColor = 2
- 输出:
[[2,2,2],[2,2,0],[2,0,1]]
- 解释:从给定单元格开始,使用深度优先搜索或广度优先搜索遍历矩阵。将相连单元格的颜色更改为新颜色。
- 输入:
- 力扣题目:
- 洪水填充(力扣第733题)
- 岛屿数量(力扣第200题)
- 被围绕的区域(力扣第130题)
-
回溯法
回溯法会探索所有可能的解决方案,当一条解决方案路径失败时,就进行回溯。当需要找到满足给定约束条件的所有(或部分)解决方案时,可使用这种模式。例如组合问题,如生成排列、组合或子集。
- 示例题目:生成给定数字列表的所有排列。
- 示例:
- 输入:
nums = [1, 2, 3]
- 输出:
[[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]
- 解释:使用递归生成排列。对于每个元素,将其包含在当前排列中,并递归生成剩余的排列。当一条路径的所有排列都生成后,进行回溯。
- 输入:
- 力扣题目:
- 全排列(力扣第46题)
- 子集(力扣第78题)
- N皇后问题(力扣第51题)
-
动态规划模式
动态规划(DP)是将问题分解为较小的子问题,并使用自底向上或自顶向下的方法解决它们。适用于具有重叠子问题和最优子结构的问题。动态规划本身有多种子模式,其中一些最重要的包括:斐波那契数列、0/1背包问题、最长公共子序列(LCS)、最长递增子序列(LIS)、子集和问题、矩阵链乘法。
- 示例题目:计算第
n
个斐波那契数。 - 示例:
- 输入:
n = 5
- 输出:
5
(前五个斐波那契数是0, 1, 1, 2, 3, 5) - 解释:使用自底向上的方法计算第
n
个斐波那契数。从最初的两个数(0和1)开始迭代计算后续数字,如(dp[i] = dp[i - 1] + dp[i - 2])
。
- 输入:
- DP动态规划的子模式:
- Fibonacci Numbers
- 0/1 Knapsack
- Longest Common Subsequence (LCS)
- Longest Increasing Subsequence (LIS)
- Subset Sum
- Matrix Chain Multiplication
- 力扣题目:
- 爬楼梯(力扣第70题)
- 打家劫舍(力扣第198题)
- 零钱兑换(力扣第322题)
- 最长公共子序列(力扣第1143题)
- 最长递增子序列(力扣第300题)
- 分割等和子集(力扣第416题)
- 示例题目:计算第