- 博客(52)
- 资源 (1)
- 收藏
- 关注
原创 代码训练营第63天:leetcode84柱状图中最大的矩形
很久没有长期坚持学习。这样的体验确实很珍贵。不能说每天都认真做题吧,但是每天都能抽出时间思考一下。基本上重要的题,之前放弃过的题也都做过看过一遍了。感觉是对算法题有了些自信。
2023-11-07 23:16:10
151
原创 代码训练营第62天:单调栈part02|leetcode503下一个更大元素|leetcode42接雨水
和之前的代码的唯一区别就是,由于是循环数组,因此相当于是遍历两边即可。实际上对i取个模就可以了。还是单调栈,就是找一个元素的左右最大元素,再去算面积。
2023-11-05 23:58:36
167
原创 代码训练营第60天:单调栈part01|leetcode739 每日温度|leetcode496 下一个更大元素
那有同学就问了,我怎么能想到用单调栈呢?什么时候用单调栈呢?。时间复杂度为O(n)。例如本题其实就是找找到一个元素右边第一个比自己大的元素,此时就应该想到用单调栈了。那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。
2023-11-04 16:34:25
226
原创 代码训练营第59天:动态规划part17|leetcode647回文子串|leetcode516最长回文子序列
如果s[i]与s[j]不相同,说明s[i]和s[j]的同时加入 并不能增加[i,j]区间回文子序列的长度,那么分别加入s[i]、s[j]看看哪一个可以组成最长的回文子序列。由于dp[i][j]的计算需要dp[i+1][j-1],由此确定了i是从大到小,j是从小到大的遍历过程。那么dp[i][j]一定是取最大的,即:dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);如果s[i]与s[j]相同,那么dp[i][j] = dp[i + 1][j - 1] + 2;
2023-11-03 08:28:58
193
原创 代码训练营第58天:动态规划part16|leetcode583两个字符串的删除操作|leetcode72编辑距离。
两个字符串,这是一个二维dp。dp[i][j]的含义是字符串a到i,字符串b到j为止,需要删除的最少操作。那无非是两种,若i与j相同,就是dp[i][j]=dp[i-1][j-1],如果不同的,那就删i:dp[i-1][j]+1, 删j:dp[i][j-1], 或者i,j 都删掉:dp[i-1][j-1]+2,这三个取最小即可。我的想法是,和上面那道题一样的,如果字母相同,就不用改,如果字母不同,就删一个dp[i-1][j]+1,增加一个dp[i][j-1]+1,或者换一个dp[i-1][j-1]+1。
2023-11-02 10:51:54
218
原创 代码训练营第57天:动态规划part15|leetcode392 判断子序列|leetcode115 不同的子序列
t[j - 1]),此时相当于t要删除元素,t如果把当前元素t[j - 1]删除,那么dp[i][j] 的数值就是 看s[i - 1]与 t[j - 2]的比较结果了,即:dp[i][j] = dp[i][j - 1];
2023-11-01 18:54:07
237
原创 代码训练营第56天:动态规划part14|leetcode1143最长公共子序列|leetcode1035不相交的线
如果text1[i - 1] 与 text2[j - 1]不相同,那就看看text1[0, i - 2]与text2[0, j - 1]的最长公共子序列 和 text1[0, i - 1]与text2[0, j - 2]的最长公共子序列,取最大的。如果text1[i - 1] 与 text2[j - 1]相同,那么找到了一个公共元素,所以dp[i][j] = dp[i - 1][j - 1] + 1;即:dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
2023-11-01 18:21:50
129
原创 代码训练营第55天:动态规划part13|leetcode300最长递增子序列|leetcode674最长连续递增子序列|leetcode718最长重复子数组
更简洁的方法是,让dp[i][j]表示i-1/j-1时的最长重复子数组,这样就省去了很多麻烦的初始化操作。我的思路是dp[i][j]的含义是第一个数组到i,第二个数组到j时他们的最长重复子数组的含义。这道题其实递推思路比较容易想到,但是再算dp[i]的时候也是需要往前看的,这是麻烦的地方。这道题其实是难度提示了我,dp数组含义不用变,但是只需要向前看一个即可。滚动数组方法有点难了。
2023-10-30 09:22:58
152
原创 代码训练营第53天:动态规划part12|leetcode309买卖股票的最佳时期含冷静期|leetcode714买卖股票的最佳时机含手续费
其实在之前的递推中,只要加入在购买的时候减去手续费就可以了。但是在这种情况下[1]就不一定是最大的,需要两个状态求max。我的理解是,股票问题其实是个自动机问题,每一步递推实际上是自动机的状态变化过程。实际上这个状态转移图一出来,逐个状态分析即可。
2023-10-29 00:23:35
222
原创 代码训练营第52天:leetcode123买卖股票的最佳时机3|leetcode188买卖股票的最佳时机4
这回要决策两次,根据之前的代码,实际上就扩展成dp[i][0],dp[i][1],dp[i][2],dp[i][3],dp[i][4]分别代表不持有,第一次持有,第一次不持有,第二次持有,第二次不持有。
2023-10-27 08:32:08
83
原创 代码训练营第51天:leetcode121买卖股票的最佳时机|leetcode122买卖股票的最佳时机2
同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i - 1][0], -prices[i]);其实一开始现金是0,那么加入第i天买入股票现金就是 -prices[i], 这是一个负数。如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来。如果第i天不持有股票即dp[i][1], 也可以由两个状态推出来。
2023-10-26 10:20:17
136
原创 代码训练营第50天:leetcode198打家劫舍|leetcode213打家劫舍2|leetcode337打家劫舍3
这道题就是直接的递归思路做。dp数组含义就是偷到第i家时能偷到的最多的钱。无非就两种情况,如果不偷第i家,那dp[i]=dp[i-1],如果偷第i家,那么第i-1家不可能偷,dp[i]=nums[i]+dp[i-2];这道题的区别就在于递推的时候首位相连了,就意味着首尾只能选一个,那其实dp的时候做两次,一次包含首,一次包含尾,取最大即可。这道题实际上就是把上面的-1变成子树,-2变成子树的子树。但是具体的遍历还真没有思路。猪脑烧了,受不了了。
2023-10-25 20:58:39
224
原创 代码训练营第49天:leetcode139单词划分|多重背包|背包讲解
就做题的感觉来看,基本上要默认对于背包问题的递推式非常熟悉,在这基础上要区分是基础,组合或者排列问题,确定具体的递推式和遍历顺序。总之对于熟练度的要求很高。
2023-10-24 13:12:57
183
原创 代码训练营地48天:动态规划part7|leetcode70爬楼梯|leetcode322零钱兑换|leetcode279完全平方数
零钱又是无限的,是一个完全背包问题。由于是求最小值,其实这道题遍历顺序和普通的完全背包就可以一样,递推公式就是dp[j] = min(dp[j - coins[i]] + 1, dp[j]);每一阶可以重复使用,例如跳了1阶,还可以继续跳1阶。1阶,2阶,.... m阶就是物品,楼顶就是背包。问跳到楼顶有几种方法其实就是问装满背包有几种方法。这里的2换成m就是最一般的完全背包问题。
2023-10-23 08:20:49
121
原创 代码训练营第46天:动态规划part6|完全背包|leetcode518零钱兑换2|leetcode377组合总和4
看了这两个图,大家就会理解,完全背包中,两个for循环的先后循序,都不影响计算dp[j]所需要的值(这个值就是下标j之前所对应的dp[j])。01背包中二维dp数组的两个for遍历的先后循序是可以颠倒了,一维dp数组的两个for循环先后循序一定是先遍历物品,再遍历背包容量。只要保证下标j之前的dp[j]都是经过计算的就可以了。对于纯粹的完全背包问题,一维数组的for循环遍历是无所谓内外的,这是和01背包区别的地方。01背包中,为避免重复放入,对于背包容量要逆序遍历,而完全背包问题就不用逆序遍历了。
2023-10-21 14:06:45
77
原创 代码训练营第45天:动态规划part05|leetcode1049最后一块石头的重量|leetcode494目标和|leetcode474一和零
按照我的理解,这道题是需要将石头分为重量接近的两堆,这样两两消除后剩下的重量就是最小的。背包的重量就是stone,价值也是stone;i++){j--){看出来是个01背包问题,dp[j]表示容量(这里说容量更形象,其实就是重量)为j的背包,最多可以背最大重量为dp[j]。可以回忆一下01背包中,dp[j]的含义,容量为j的背包,最多可以装的价值为 dp[j]。
2023-10-20 08:20:56
105
原创 代码训练营第44天:动态规划part4|01背包|01背包(一维数组)|leetcode416分割等和子集
如果放物品i,那么就是放i之前的物品的价值加上i物品的价值,即dp[i-1][j-weight[i]]+value[i];因为对于二维dp,dp[i][j]都是通过上一层即dp[i - 1][j]计算而来,本层的dp[i][j]并不会被覆盖!当j >= weight[0]时,dp[0][j] 应该是value[0],因为背包容量放足够放编号0物品。如果不放i物品,dp[i][j]就是放i之前物品的价值,即dp[i-1][j],根据递推式,很明显dp[i][j]需要左上角的这些元素,因此正常遍历一遍即可。
2023-10-19 10:30:09
119
原创 代码训练营第43天:动态规划part3|leetcode343整数拆分|leetcode96不同的二叉搜索树
这里我只初始化dp[2] = 1,从dp[i]的定义来说,拆分数字2,得到的最大乘积是1,这个没有任何异议!dp[i-j]*j,根据数组定义,仍然可以合并成i这个数字,但是就是[i-j]拆分的结果和j相乘了。dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量]严格从dp[i]的定义来说,dp[0] dp[1] 就不应该初始化,也就是没有意义的数值。(i-j)*j指的是两个数相乘,他们符合i-j+j=i,是原来的数字。4,确定遍历顺序:i是从3到n,j是从1到i-1;
2023-10-18 15:56:56
153
原创 代码训练营第42天:动态规划part2|leetcode62不同路径|leetcode63不同路径2
和昨天的那几道题差不多。和上面那道题区别在于确定在障碍的下方和右边的方案数量。在障碍的下方,意味着只能从左边过来,由于每次只能往下或者往右边走,其实相当于是到每个格子的方案等于到上面的加上到左边的,即。实际做的时候发现很多数据点过不了,陷入了加入if来特判的循环怪圈。其实只是有一点没想通,针对障碍的处理没想明白就导致思路整个走歪了。
2023-10-17 00:46:17
92
原创 代码训练营第41天:动态规划part1|leetcode506斐波那契|leetcode70爬楼梯|leetcode746使用最小花费爬楼梯
按照方法论过了一次,确实对递推更理解了。3,leetcode70 爬楼梯由于一次可以爬一个或者两个,那其实每次要爬到i,可以从i-1爬上来,也可以从i-2爬上来。那就是i-1的方案和i-2的方案的和。看上去和上面那个斐波那契很像,但是是完全不同的思考思路。和上面那个思路相似。爬上当前楼层有两种方案,从i-1或者i-2爬上来,支付的费用应该是min(dp[i-1]+cost[i-i], dp[i-2]+cost[i-2]);随想录给出的改进版本,是利用了这个递推过程只用到了前两个的特点:
2023-10-17 00:03:17
78
原创 代码训练营第39天:贪心算法part6|leetcode739单调递增的数字|贪心总结
题做了一遍后发现找到局部最优到整体最优这个思路,其实在很多题里面都藏的很深。有时候很难确定哪一部分的是可以最优化的局部。还是需要都做题啊。
2023-10-14 21:54:01
62
原创 代码训练营第38天:贪心算法part05|leetcode435无重叠区间|leetcode763划分字母区间|leetcode56合并区间
这个最后的过程很奇妙,对于当前字母,如果它最远出现的位置就是当前位置,就画出来,实际上这只保证了针对当前字母的划分,但是它莫名其妙就能保证是全局最优的划分。我的思路还是沿用昨天那道题的方法,对区间排序。判断重叠区间的方法就是比较排序后的每一个区间起点和之前最远处的区间端点。我的思路还是,先按照左区间端点排序,排序后记录当前最远连续点,比较区间起点和最远连续点。比随想录麻烦一些,但是思路都是一致的。呃,原来还挺容易的。
2023-10-13 08:29:44
108
原创 代码训练营第37天:贪心算法part04|leetcode860柠檬水找零|leetcode406根据身高重建队列|leetcode452用最少数量的箭引爆气球
这个更新区间的方法没想到。这道题我开始想成了看有多少段连续区间,仔细想想才理解它要求找有几个重叠的区间。说实话,这道题没读懂要干什么,但是大概看出来排列应该是按身高降序排列,k升序排列的方法。虽然说是队列,但是不是真的用队列,因为插入的时候也不是只在首尾操作。和代码随想录思路一致。
2023-10-12 10:21:04
129
原创 代码训练营第36天:贪心算法part03|leetcode1005K次取反后最大化的数组和|leetcode134加油站|leetcode135分发糖果
和之前遇到的某道题思路有点像,从左往右遍历看见右边比左边大的,从右往左遍历看见左边比右边大的,candy取两次遍历中最大的那个,确保符合规则2,另一种贪心思路也很有意思,是看从0开始,如果出现油不够用了,就往后找,看哪个节点能把这个负数填平,能把这个负数填平的节点就是出发节点。代码随想录的代码时间效率更高,其实反复取反那一步,只需要剩下的k是奇数取反,偶数就保持就可以了。实际上,反过来考虑,只有有一个位置出现了加的油不够用,就必须从这个位置后面的某一处开始,可以按照这个思路从0遍历,
2023-10-11 16:24:20
68
原创 代码训练营第35天:贪心算法part2|leetcode122买卖股票的最佳时机2|leetcode55跳跃游戏|leetcode45跳跃游戏2
这道题的贪心思路也不复杂,从第一个点出发,遍历能到达的所有点,找到这些点中能到最远未知的点作为下一次遍历的起点,最终如果超过了数组长度就能到达,没超过就不能到。如果移动下标等于当前覆盖最大距离下标, 需要再走一步(即 ans++),因为最后一步一定是可以到的终点。如果移动下标不等于当前覆盖最大距离下标,说明当前覆盖最远距离就可以直接达到终点了,不需要再走一步。最后的步数就是最少步数。这道题我开始想的是,55题每次找最远就是最优的思路,只要统计次数就行,后来发现不对,例如。注意那个循环终止条件。
2023-10-10 08:35:58
116
原创 代码训练营第34天:贪心算法part1|leetcode455分发饼干|leetcode376摆动序列|leetcode53最大子序和
这道题的思路肯定是,遇到正数和是变大的,要加上去,遇到负数和会变小,就要考虑加不加上。但是第二个例子中也展示了,最终的子序列中可能有负数。贪心就从局部最优推导出全局最优。因此有些问题贪心是高效率的,有些问题贪心是有误导性的。这道题就是按照局部最优推导全部最优的思路,把符合条件中尽可能小的分出去。另一种方式,把大的先分给最能吃的也是这个思路。确实,这道题应该是用负数去找子序列的起始点。第一次提交卡在了[0,0]这个测试用例上。原来是cur不能为0,忽略了这点。
2023-10-09 08:27:24
63
原创 代码训练营第32天:回溯算法part6|leetcode332重新安排行程|第51题N皇后|第37题解数独
回溯解决的问题:组合问题:N个数里面按一定规则找出k个数的集合排列问题:N个数按一定规则全排列,有几种排列方式切割问题:一个字符串按一定规则有几种切割方式子集问题:一个N个数的集合里有多少符合条件的子集棋盘问题:N皇后,解数独等等本质:递归的暴力搜索。void backtracking(参数) {if (终止条件) {存放结果;return;for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表);
2023-10-07 12:32:18
82
原创 代码训练营第31天:回溯算法part5|leetcode491递增子序列|leetcode46全排列|leetcode47全排列2
这道题和昨天的题很像,但是区别是这一次不能先排序,如果排了序就所有子集都是递增子序列了。在去重判断中和昨天的不同在于,这一次insert进used的数在本层递归结束前不应该再使用一遍,所以回溯的时候不能直接pop,而是让递归自己回到上一层析构掉used数组。莫名其妙过不了,仔细对比随想录的代码才发现不能在上面加上return,确实因为是找所有子集,找到一个后还需要继续往下找。排列问题里的used数组是看一个排列里面不要有重复,而这道题的要求是对所有结果去重复,如果要对树层中前一位去重,就用。
2023-10-06 22:17:29
66
原创 代码训练营第30天:回溯算法part4|leetcode93复原ip地址|leetcode73子集|leetcode90子集2
感觉这个字符串分割还是很麻烦。需要在递归过程中处理字符串。这道题只需要在上面那题加上昨天用上的used数组去重。求所有子集,遍历整个数的节点就可以了。看了代码随想录的讲解。
2023-10-05 22:22:16
92
原创 代码随想录第29天:回溯算法part3|leetcode39组合总和|leetcode40组合总和2|leetcode131 分割回文串
都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上使用过,一个维度是同一树层上使用过。回溯去重复的逻辑就是,不使用上一层已经使用过的元素。candidate=1,2,2,3 target = 4 这时13,22都是答案,因此不能直接对candidate去重。代码随想录的剪枝起点也是这样,但是他是直接在for上做剪枝,这样的好处是就不需要进入下一层递归直接跳过。回看一下题目,元素在同一个组合内是可以重复的,怎么重复都没事,但两个组合不能相同。
2023-10-04 21:34:29
241
原创 代码训练营第28天:回溯算法part2|leetcode216组合总和3|leetcode17电话号码里的字母组合
没有做任何剪枝,而且在开始调试的时候总是额外输出最后一个数字对应的所有字母单独出现在result中,后来在return逻辑上判断长度解决了这个问题。因为问的是所有可能,总要都遍历一遍的。分析一下,最外层的for循环的作用是遍历digits,但是在这个题目中递归函数start参数已经实现了逐个元素遍历digits,意味着start和i表达的意义完全相同。如果像代码随想录里面第二种剪枝写法,即写在for循环里,就需要先回溯再剪枝。最后是完整的加上错误处理的代码,错误处理在面试的算法中应该写出来。
2023-10-03 23:20:06
204
原创 代码训练营第27天:回溯算法part1|leetcode77 组合
理论讲解:回溯算法leetcode77:组合目录1,回溯算法理论基础:2,leetcode77 组合3,剪枝操作1,回溯算法理论基础:从今天开始的算法篇就是我不熟练的部分,需要学一学理论了。视频讲解回溯-递归函数下面部分就是回溯的逻辑 回溯函数=递归函数回溯搜索法:纯暴力搜索:可以解决组合问题,切割问题,子集问题,排列问题,棋盘问题for循环嵌套解决不了回溯法都可以抽象为一个树结构。回溯算法模版:void backtracking
2023-10-02 21:35:15
274
原创 代码训练营第23天:二叉树part09|leetcode669修剪二叉搜索树|leetcode108将有序数组转化为二叉搜索树|leetcode538把二叉搜索树转换为累加树
由于要求是高度平衡的二叉树,因此思路应该和二分查找一样,在两端不断寻找均值作为节点,我的递归思路是当前节点-左-右分别递归寻找,终止条件是二分查找的终止条件。递归思路很巧妙,就是在左右边找符合区间内的节点,找到后直接连给当前节点的left和right子树。仔细看示例,他这个累加方法是右中左这么累加下去,每一次就是当前节点的值加上一个累加结果,这就很简单了。注意前两个递归是确保当前的root节点在low, high之中,后两个递归才是连子树。这道题比较难理解和难做的是,在修建后要保持原有的结构。
2023-09-28 10:36:30
369
1
原创 代码训练营第22天:二叉树part8|leetcode235 二叉搜索树的最近公共祖先|leetcode701 二叉搜索树中的插入操作|leetcode450 删除二叉搜索树中的节点。
数据结构课上最难的一个问题了,分三种情况,左右都空就直接删除,左右有一个空的就让那个非空的补上,左右都非空就让一个接在另一个上面。唯一需要注意的是这个移动逻辑。比两个都大就往小的找,往左走。bst是的特点是数值是有序的,意味着如果这个公共祖先存在,就一定介于p和q之间。免去了我在对于可插入节点的判断。走到nullptr的地方创建就好了。常规操作了,但是写的时候还是遇到了一些bug,不过总体还是很简单的。只要找到第一个介于p和q之间的就是找到的。至于这个迭代法应该是数据结构课上写的,有时间再复习一下。
2023-09-27 10:57:37
515
1
原创 代码训练营第21天:二叉树part7|leetcode530二叉搜索树的最小绝对差|leetcode501二叉搜索树的众数|leetcode236二叉树的最近公共祖先
如果出现了更大的,那么就重新记录结果集。在图中,这个节点7由于左右找到了pq,因此他的left和right返回值不是null,并且每一层往上返回,由于都在7的子树上,7会一直向上返回,直到遍历整棵树发现只有7是左右不是null,意味着7的左右有pq并且深度是最大的那个,自己就返回了。有意思的地方在于,代码中的left和right不是简单的左右子树的意思,而是pq节点所在的节点的父节点。开始我想的是只需要递归比较当前节点和左右子节点的大小,找到最小的差值即可,但是写完后才想到,最小差值不一定来自于相邻节点。
2023-09-26 09:11:12
477
1
原创 代码随想录第20天:二叉树part6|leetcode654最大二叉树|leetcode617合并二叉树|leetcode700二叉搜索树中的搜索|leetcode98验证二叉搜索树。
经过几天二叉树刷题,感觉渐入佳境,递归写的越来越熟练了。以前不敢写的现在也敢写了。在训练营中的坚持确实是有用的。
2023-09-25 09:01:33
546
1
原创 代码训练营第18天|二叉树part5|leetcode513找树左下角的值|leetcode112,113路径总和|leetcpde105,106遍历序列构造二叉树
。
2023-09-23 15:31:17
642
1
原创 代码训练营第17天|二叉树part4|leetcode110平衡二叉树|leetcode257二叉树的所有路径|leetcode404左叶子之和
这种写法中用-1代表不符合要求,也就是已经不平衡的情况。而在递归中,如果左右子树已经都不平衡了,那就直接return出递归函数返回false,如果左右子树都是平衡的,再比较左右子树的高度差。最终如果不平衡返回-1,就一路返回到isBalanced中,如果是平衡的,就把现在这个子树的高度返回出去,在上一层递归继续比较。这道题一眼使用深度优先遍历一遍即可,注意是从根节点开始,有点像根左右的方法,到了叶子结点也就是叶条路走到头了,可以放到结果数组中形成一条路径了。递归的方法周末再看了。
2023-09-22 01:02:13
536
1
原创 代码训练营第15天|二叉树part2|层序遍历10道题|leetcode226翻转二叉树|leetcode101对称二叉树
10道题确实很相似,本质上都是使用bfs去解决问题。但是刷起来确实很爽。
2023-09-20 22:35:29
1056
2
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅