回溯
2、4、5、7、8、9、10、11、13、14、15、16、
回溯就是:横向遍历(for循环),纵向递归(for循环里面的回溯方法)的组合。
对于全排列,for循环从零开始遍历,并且使用used数组
为了防止出现重复的结果,使用startIndex (第一棵子树选了2、3,第二棵子树选了3,就不能再选2)
回溯法的代码框架:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点; (eg:字符串新加入一个字符、设置该位置已读等等)
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果 (eg:去除之前新加入的字符、取消该位置的已读等等)
}
}回溯问题分类:
子集、组合 (startIndex)
全排列 (used数组)
全排列问题去重:
“树间横向去重”:
47. 全排列 II (树层去重+树枝去除已使用)
//存在重复数字 Arrays.sort(nums); if(used[i] || (i>0&&nums[i]==nums[i-1]&&used[i-1]==false)){ continue; }“子树纵向去重”:
46. 全排列 (树枝去除已使用)
if(used[i] == true)如图,同一树枝上(backtrack())可以重复选,而同一父节点的相同层上(for循环)的不可以重复选取元素。
// used[i] == true,说明同一树枝nums[i]使用过 (同一树枝是在同一个backtrack方法里的)
// used[i] == false,说明同一树层nums[i]使用过(同一树层是在不同拔backtrack方法里面的,或者说他前面的那条树枝的backtrack方法已经重置了状态才轮到现在这个树枝)
简单
中等
491. 非递减子序列 (类似树层区中)
46. 全排列 (树枝去除已使用)
47. 全排列 II (树层去重+树枝去除已使用)
113. 路径总和 II (二叉树的回溯,和列表的回溯一样,backtrack=终止条件+遍历backtrack)
链表
2、
简单
中等
双指针
1、2、4、5、6、7、8、9、10
简单
中等
146. LRU 缓存 (LinkedHashMap)
贪心算法 (局部最优推导全局最优)
2、4、7、8、9、11、13、14、18、19、20
简单
中等
困难
动态规划 (重叠子问题,状态推导)
tips:
题目让求“最大”,你就要准备好在dp数组初始化为Integer.MIN_VALUE,方便之后比较大小。
但是,很可能需要把dp[0]置为0.
Arrays.fill(dp,Integer.MIN_VALUE);
dp[0]=0;动态规划,英文:Dynamic Programming,简称DP,如果某一问题有很多重叠子问题,使用动态规划是最有效的。
所以动态规划中每一个状态一定是由上一个状态推导出来的,这一点就区分于贪心,贪心没有状态推导,而是从局部直接选最优的。
7、8、13、16、19、21、26、29、30、32、34、41、42、43、44、46、47、50、52、53
背包问题:
dp[i][j]:i代表物品,j代表背包容量,表示从下标为[0-i]的物品里任意取,放进容量为j的背包,价值总和最大是多少。有的题没有“价值”,或者说价值就是“种数”(求有多少种xx之类的)
0-1背包:每个物品只有一个。
递归公式:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
完全背包:每个物品可以有多个(取用多次)。
递推公式:
dp[i][j] = max(dp[i - 1][j], dp[i][j - weight[i]] + value[i]);
0-1背包递推公式可以压缩为1维:
dp[j]表示:容量为j的背包,所背的物品价值可以最大为dp[j],那么dp[0]就应该是0,因为背包容量为0所背的物品的最大价值就是0。
递推公式:dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
遍历顺序:物品遍历的for循环放在外层,遍历背包的for循环放在内层,且内层for循环倒序遍历!
遍历顺序:
如果求组合数就是外层for循环遍历物品,内层for遍历背包。
如果求排列数就是外层for遍历背包,内层for循环遍历物品。
递推方法:
这里我们dp[1][4]的状态来举例:
求取 dp[1][4] 有两种情况:
放物品1
还是不放物品1
如果不放物品1, 那么背包的价值应该是 dp[0][4] 即 容量为4的背包,只放物品0的情况。
如果放物品1, 那么背包要先留出物品1的容量,目前容量是4,物品1 的容量(就是物品1的重量)为3,此时背包剩下容量为1。
容量为1,只考虑放物品0 的最大价值是 dp[0][1],这个值我们之前就计算过。
初始化:
如果背包容量j为0的话,即dp[i][0],无论是选取哪些物品,背包价值总和一定为0。
状态转移方程 可以看出i 是由 i-1 推导出来,那么i为0的时候就一定要初始化。
dp[0][j],即:i为0,存放编号0的物品的时候,各个容量的背包所能存放的最大价值。
那么很明显当 j < weight[0]的时候,dp[0][j] 应该是 0,因为背包容量比编号0的物品重量还小。
当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。
1、0/1背包:外循环nums,内循环target,target倒序且target>=nums[i];
2、完全背包:外循环nums,内循环target,target正序且target>=nums[i];
3、组合背包(考虑顺序):外循环target,内循环nums,target正序且target>=nums[i];
4、分组背包:这个比较特殊,需要三重循环:外循环背包bags,内部两层循环根据题目的要求转化为1,2,3三种背包类型的模板
1、最值问题: dp[i] = max/min(dp[i], dp[i-nums]+1)或dp[i] = max/min(dp[i], dp[i-num]+nums);
2、存在问题(bool):dp[i]=dp[i]||dp[i-num];
3、组合问题:dp[i]+=dp[i-num];
链接:https://leetcode.cn/problems/coin-change/solutions/778891/yi-pian-wen-zhang-chi-tou-bei-bao-wen-ti-sq9n/
背包问题中等
518. 零钱兑换 II (求组合数,外层物品,内层背包)
377. 组合总和 Ⅳ (求排列数:外层遍历背包,内层遍历物品) (完全可以视为爬楼梯的问题)
139. 单词拆分 (也可以想象成爬楼梯,能不能爬到第i阶台阶?当时看dp[i-len[j]]是不是true啦)
简单
392. 判断子序列 (我的写法是判断a和b字符串的最长公共子串是不是长度等于b)
中等
279. 完全平方数 (也是上楼梯的变种,只不过步距没有明着写出来,这道题的步距是完全平方数,外层遍历楼梯,内层遍历步距)
343. 整数拆分 (思想类似279. 完全平方数 ,还是上楼梯,只不过是求乘积 )
二分查找
二分查找的平均时间复杂度:O (log n)
lowerBound(): 查找第一个>=target的数的下标
private int lowerBound(int[] nums, int target) {
int left = 0;
int right = nums.length - 1; // 闭区间 [left, right]
while (left <= right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right+1] >= target
int mid = left + (right - left) / 2;
if (nums[mid] >= target) {
right = mid - 1; // 范围缩小到 [left, mid-1]
} else {
left = mid + 1; // 范围缩小到 [mid+1, right]
}
}
// 循环结束后 left = right+1
// 此时 nums[left-1] < target 而 nums[left] = nums[right+1] >= target
// 所以 left 就是第一个 >= target 的元素下标
return left;
}由高亮部分代码,我们可知:
最终nums[r+1]一定>=target, 而nums[l-1]一定<target, 故而,最终应该return l,而l也是在这个非递减数组中第一个>=target的下标,因此方法名字叫做:lowerBound()
当数组中元素都<target时,将会返回nums.length
简单
中等
困难
矩阵
中等
子串
中等
560. 和为 K 的子数组 (前缀和)(数组不是单调的话,不要用滑动窗口,考虑用前缀和)
滑动窗口
中等
困难
数组
中等
238. 除了自身以外数组的乘积 (前缀积,后缀积)
困难
BFS
中等
207. 课程表 (拓扑图,入度表+邻接表+入度为零队列)
DFS
中等
二叉树
简单
中等
栈
简单
中等
739. 每日温度 (单调栈)
239. 滑动窗口最大值 (单调栈)
按照日期记录
26-4-19
数组
栈
739. 每日温度 (单调栈)
239. 滑动窗口最大值 (单调栈)
26-4-18
链表
Bfs
滑动窗口
动态规划
数组
26-4-14
26-4-7
bfs
207. 课程表 (拓扑图,入度表+邻接表+入度为零队列)
二叉树
Dfs
栈
26-4-6
技巧
链表
146. LRU 缓存 (LinkedHashMap)
二分查找
矩阵
哈希
子串
560. 和为 K 的子数组 (前缀和)
滑动窗口
数组
238. 除了自身以外数组的乘积 (前缀积,后缀积)
26-4-5
26-4-4
26-4-3
26-4-2
回溯
链表
双指针
二叉树
字符串
双指针
26-4-1
25-07-11
25-07-12
动态规划
回溯
链表
25-07-13
25-07-14
25-07-17
25-07-18
双指针
25-07-19
25-07-20
25-07-21
回溯:
动态规划
25-07-22
动态规划
双指针
25-07-23
25-07-24
递归
动态规划
滑动窗口:
25-07-26
25-07-28
动态规划
数组
链表
148. 排序链表 (归并排序)
25-07-29
图
207. 课程表(拓扑排序)
208. 实现 Trie (前缀树)(字典树)
栈
双指针
哈希表
二分查找
25-08-14
字符串
双指针
二分查找
DFS岛屿问题
25-08-15
二分查找
动态规划
背包问题
双指针
647. 回文子串 (中心扩散法求回文子串数量)
25-08-16
25-08-18
25-08-19
字符串
双指针
动态规划:
贪心
402. 移掉 K 位数字 (用到了单调栈)
回溯
模拟
25-08-21
字符串
179. 最大数 (自定义排序规则)
链表环
动态规划
回溯
113. 路径总和 II (二叉树的回溯,和列表的回溯一样,backtrack=终止条件+遍历backtrack)
25-08-22
25-08-24
25-08-25
25-08-26:
25-08-27
25-08-28
25-08-29
链表
动态规划
字符串
最大堆
哈希表
回溯
9-5
set去重,最小堆
8-26 - Krahets 笔面试精选 88 题
二叉树搜索
中、前、后续遍历
230. 二叉搜索树中第 K 小的元素 (中序遍历,好题,给了我启发)
层序遍历
深度遍历
236. 二叉树的最近公共祖先 (深度遍历记录路径)
8-26 - Krahets 笔面试精选 88 题
8-7 - Krahets 笔面试精选 88 题
8-5 - Krahets 笔面试精选 88 题
双指针
普通的单序列双指针
双序列,每个序列一个指针
快慢指针
滑动窗口
三指针(固定一个,挪动另外两个双指针)
8-2 - Krahets 笔面试精选 88 题
动态规划
普通的不断从前到后更新状态:
dp[i] 和 dp[i-1]
dp[i] 和 dp[i-1] 和 dp[i-2]
dp[i] 和 dp[:i]
7-31
dfs
7-30
7-26
7-25
动态规划
回溯
双指针
7-24
2024.7.23
hash
3-28
3-27
二叉树-递归-dfs
二分查找
3-26
hash
3-25
3-24
归并排序
逆序对
动态规划
dfs
双指针
滑动窗口
喜马拉雅-后端开发-编程题-1:239. 滑动窗口最大值
双指针
喜马拉雅-后端开发-编程题2:LCR 078. 合并 K 个升序链表
3-19
链表
143. 重排链表 (逆转链表+合并链表+链表中点,三合一)
3-18
动态规划
自定义排序
3-17
3-16
链表
hash表
排序
动态规划
3-15
3-14
双指针
动态规划
前缀和
优先队列
3-13
3-12
回溯
组合
组合总和
递增子序列
动态规划
二叉树
二叉树的最近公共祖先
3-1
2-29
2-28
贪心
动态规划
2-27
回溯
子集
组合
排列
47. 全排列 II (建议看代码随想录的代码,注释很好帮助理解)
分割
递增子序列 (没看懂)
2-26
BFS
DFS
剑指 Offer 12. 矩阵中的路径 (暴力DFS搜索)
236. 二叉树的最近公共祖先(dfs记录一条从上至下的顺溜的搜索路径)
200. 岛屿数量 (搜索连通量的个数)
贪心
53. 最大子数组和(这道题去看代码随想录)