diff --git "a/problems/0037.\350\247\243\346\225\260\347\213\254.md" "b/problems/0037.\350\247\243\346\225\260\347\213\254.md" index aaac4c7cc1..18a96d581b 100644 --- "a/problems/0037.\350\247\243\346\225\260\347\213\254.md" +++ "b/problems/0037.\350\247\243\346\225\260\347\213\254.md" @@ -32,6 +32,12 @@ * 你可以假设给定的数独只有唯一解。 * 给定数独永远是 9x9 形式的。 +# 算法公开课 + +**《代码随想录》算法视频公开课:[回溯算法二维递归?解数独不过如此!| LeetCode:37. 解数独](https://www.bilibili.com/video/BV1TW4y1471V/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + + ## 思路 棋盘搜索问题可以使用回溯法暴力搜索,只不过这次我们要做的是**二维递归**。 diff --git "a/problems/0046.\345\205\250\346\216\222\345\210\227.md" "b/problems/0046.\345\205\250\346\216\222\345\210\227.md" index c437f7ca23..86bc704d4b 100644 --- "a/problems/0046.\345\205\250\346\216\222\345\210\227.md" +++ "b/problems/0046.\345\205\250\346\216\222\345\210\227.md" @@ -23,9 +23,13 @@ [3,2,1] ] -## 思路 -**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。 +# 算法公开课 + +**《代码随想录》算法视频公开课:[组合与排列的区别,回溯算法求解的时候,有何不同?| LeetCode:46.全排列](https://www.bilibili.com/video/BV19v4y1S79W/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + +## 思路 此时我们已经学习了[77.组合问题](https://programmercarl.com/0077.组合.html)、 [131.分割回文串](https://programmercarl.com/0131.分割回文串.html)和[78.子集问题](https://programmercarl.com/0078.子集.html),接下来看一看排列问题。 diff --git "a/problems/0047.\345\205\250\346\216\222\345\210\227II.md" "b/problems/0047.\345\205\250\346\216\222\345\210\227II.md" index d9fe8f35ef..3eb99948ea 100644 --- "a/problems/0047.\345\205\250\346\216\222\345\210\227II.md" +++ "b/problems/0047.\345\205\250\346\216\222\345\210\227II.md" @@ -4,9 +4,8 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-# 排列问题(二) -## 47.全排列 II +# 47.全排列 II [力扣题目链接](https://leetcode.cn/problems/permutations-ii/) @@ -27,10 +26,13 @@ * 1 <= nums.length <= 8 * -10 <= nums[i] <= 10 -## 思路 -**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。 +# 算法公开课 + +**《代码随想录》算法视频公开课:[回溯算法求解全排列,如何去重?| LeetCode:47.全排列 II](https://www.bilibili.com/video/BV1R84y1i7Tm/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 +## 思路 + 这道题目和[46.全排列](https://programmercarl.com/0046.全排列.html)的区别在与**给定一个可包含重复数字的序列**,要返回**所有不重复的全排列**。 这里又涉及到去重了。 diff --git "a/problems/0051.N\347\232\207\345\220\216.md" "b/problems/0051.N\347\232\207\345\220\216.md" index 16fdf34787..624452fd2c 100644 --- "a/problems/0051.N\347\232\207\345\220\216.md" +++ "b/problems/0051.N\347\232\207\345\220\216.md" @@ -5,7 +5,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-# 第51题. N皇后 +# 51. N皇后 [力扣题目链接](https://leetcode.cn/problems/n-queens/) @@ -28,10 +28,12 @@ n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上, * 输入:n = 1 * 输出:[["Q"]] -## 思路 +# 算法公开课 + +**《代码随想录》算法视频公开课:[这就是传说中的N皇后? 回溯算法安排!| LeetCode:51.N皇后](https://www.bilibili.com/video/BV1Rd4y1c7Bq/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 -**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。 +## 思路 都知道n皇后问题是回溯算法解决的经典问题,但是用回溯解决多了组合、切割、子集、排列问题之后,遇到这种二维矩阵还会有点不知所措。 diff --git "a/problems/0062.\344\270\215\345\220\214\350\267\257\345\276\204.md" "b/problems/0062.\344\270\215\345\220\214\350\267\257\345\276\204.md" index 2367587c25..760d3fac28 100644 --- "a/problems/0062.\344\270\215\345\220\214\350\267\257\345\276\204.md" +++ "b/problems/0062.\344\270\215\345\220\214\350\267\257\345\276\204.md" @@ -43,6 +43,10 @@ * 1 <= m, n <= 100 * 题目数据保证答案小于等于 2 * 10^9 +# 算法公开课 + +**《代码随想录》算法视频公开课:[动态规划中如何初始化很重要!| LeetCode:62.不同路径](https://www.bilibili.com/video/BV1ve4y1x7Eu/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + ## 思路 ### 深搜 @@ -237,7 +241,6 @@ public: 然后在给出动规的方法,依然是使用动规五部曲,这次我们就要考虑如何正确的初始化了,初始化和遍历顺序其实也很重要! -就酱,循序渐进学算法,认准「代码随想录」! ## 其他语言版本 diff --git "a/problems/0063.\344\270\215\345\220\214\350\267\257\345\276\204II.md" "b/problems/0063.\344\270\215\345\220\214\350\267\257\345\276\204II.md" index 9aa369563d..68010140eb 100644 --- "a/problems/0063.\344\270\215\345\220\214\350\267\257\345\276\204II.md" +++ "b/problems/0063.\344\270\215\345\220\214\350\267\257\345\276\204II.md" @@ -43,6 +43,10 @@ * 1 <= m, n <= 100 * obstacleGrid[i][j] 为 0 或 1 +# 算法公开课 + +**《代码随想录》算法视频公开课:[动态规划,这次遇到障碍了| LeetCode:63. 不同路径 II](https://www.bilibili.com/video/BV1Ld4y1k7c6/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + ## 思路 @@ -200,7 +204,6 @@ public: 也有一些小细节,例如:初始化的部分,很容易忽略了障碍之后应该都是0的情况。 -就酱,「代码随想录」值得推荐给身边学算法的同学朋友们,关注后都会发现相见恨晚! ## 其他语言版本 diff --git "a/problems/0070.\347\210\254\346\245\274\346\242\257.md" "b/problems/0070.\347\210\254\346\245\274\346\242\257.md" index fb23be7d48..14aeef0142 100644 --- "a/problems/0070.\347\210\254\346\245\274\346\242\257.md" +++ "b/problems/0070.\347\210\254\346\245\274\346\242\257.md" @@ -5,6 +5,7 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

# 70. 爬楼梯 + [力扣题目链接](https://leetcode.cn/problems/climbing-stairs/) 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 @@ -163,7 +164,7 @@ public: 这道题目还可以继续深化,就是一步一个台阶,两个台阶,三个台阶,直到 m个台阶,有多少种方法爬到n阶楼顶。 -这又有难度了,这其实是一个完全背包问题,但力扣上没有这种题目,所以后续我在讲解背包问题的时候,今天这道题还会从背包问题的角度上来再讲一遍。 +这又有难度了,这其实是一个完全背包问题,但力扣上没有这种题目,所以后续我在讲解背包问题的时候,今天这道题还会从背包问题的角度上来再讲一遍。 如果想提前看一下,可以看这篇:[70.爬楼梯完全背包版本](https://programmercarl.com/0070.%E7%88%AC%E6%A5%BC%E6%A2%AF%E5%AE%8C%E5%85%A8%E8%83%8C%E5%8C%85%E7%89%88%E6%9C%AC.html) 这里我先给出我的实现代码: @@ -185,7 +186,7 @@ public: 代码中m表示最多可以爬m个台阶。 -**以上代码不能运行哈,我主要是为了体现只要把m换成2,粘过去,就可以AC爬楼梯这道题,不信你就粘一下试试,哈哈**。 +**以上代码不能运行哈,我主要是为了体现只要把m换成2,粘过去,就可以AC爬楼梯这道题,不信你就粘一下试试**。 **此时我就发现一个绝佳的大厂面试题**,第一道题就是单纯的爬楼梯,然后看候选人的代码实现,如果把dp[0]的定义成1了,就可以发难了,为什么dp[0]一定要初始化为1,此时可能候选人就要强行给dp[0]应该是1找各种理由。那这就是一个考察点了,对dp[i]的定义理解的不深入。 diff --git "a/problems/0078.\345\255\220\351\233\206.md" "b/problems/0078.\345\255\220\351\233\206.md" index b5958e65e9..b0c51d4496 100644 --- "a/problems/0078.\345\255\220\351\233\206.md" +++ "b/problems/0078.\345\255\220\351\233\206.md" @@ -27,6 +27,11 @@   [] ] +# 算法公开课 + +**《代码随想录》算法视频公开课:[回溯算法解决子集问题,树上节点都是目标集和! | LeetCode:78.子集](https://www.bilibili.com/video/BV1U84y1q7Ci),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + # 思路 求子集问题和[77.组合](https://programmercarl.com/0077.组合.html)和[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)又不一样了。 diff --git "a/problems/0090.\345\255\220\351\233\206II.md" "b/problems/0090.\345\255\220\351\233\206II.md" index 57cfd09dbe..5df479869f 100644 --- "a/problems/0090.\345\255\220\351\233\206II.md" +++ "b/problems/0090.\345\255\220\351\233\206II.md" @@ -4,9 +4,8 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

-# 子集问题(二) -## 90.子集II +# 90.子集II [力扣题目链接](https://leetcode.cn/problems/subsets-ii/) @@ -26,6 +25,10 @@ [] ] +# 算法公开课 + +**《代码随想录》算法视频公开课:[回溯算法解决子集问题,如何去重?| LeetCode:90.子集II](https://www.bilibili.com/video/BV1vm4y1F71J/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + ## 思路 diff --git "a/problems/0093.\345\244\215\345\216\237IP\345\234\260\345\235\200.md" "b/problems/0093.\345\244\215\345\216\237IP\345\234\260\345\235\200.md" index 97178cd5b1..1cf152c2b5 100644 --- "a/problems/0093.\345\244\215\345\216\237IP\345\234\260\345\235\200.md" +++ "b/problems/0093.\345\244\215\345\216\237IP\345\234\260\345\235\200.md" @@ -41,6 +41,11 @@ * s 仅由数字组成 +# 算法公开课 + +**《代码随想录》算法视频公开课:[回溯算法如何分割字符串并判断是合法IP?| LeetCode:93.复原IP地址](https://www.bilibili.com/video/BV1XP4y1U73i/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + # 思路 做这道题目之前,最好先把[131.分割回文串](https://programmercarl.com/0131.分割回文串.html)这个做了。 diff --git "a/problems/0096.\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" "b/problems/0096.\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" index 519fd3237f..99a4b8dda3 100644 --- "a/problems/0096.\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" +++ "b/problems/0096.\344\270\215\345\220\214\347\232\204\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221.md" @@ -14,6 +14,11 @@ ![](https://img-blog.csdnimg.cn/20210113161941835.png) +# 算法公开课 + +**《代码随想录》算法视频公开课:[动态规划找到子状态之间的关系很重要!| LeetCode:96.不同的二叉搜索树](https://www.bilibili.com/video/BV1eK411o7QA/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + ## 思路 这道题目描述很简短,但估计大部分同学看完都是懵懵的状态,这得怎么统计呢? diff --git "a/problems/0134.\345\212\240\346\262\271\347\253\231.md" "b/problems/0134.\345\212\240\346\262\271\347\253\231.md" index ade847730c..9762dc9f2f 100644 --- "a/problems/0134.\345\212\240\346\262\271\347\253\231.md" +++ "b/problems/0134.\345\212\240\346\262\271\347\253\231.md" @@ -48,7 +48,7 @@ ## 暴力方法 -暴力的方法很明显就是$O(n^2)$的,遍历每一个加油站为起点的情况,模拟一圈。 +暴力的方法很明显就是O(n^2)的,遍历每一个加油站为起点的情况,模拟一圈。 如果跑了一圈,中途没有断油,而且最后油量大于等于0,说明这个起点是ok的。 @@ -119,8 +119,8 @@ public: } }; ``` -* 时间复杂度:$O(n)$ -* 空间复杂度:$O(1)$ +* 时间复杂度:O(n) +* 空间复杂度:O(1) **其实我不认为这种方式是贪心算法,因为没有找出局部最优,而是直接从全局最优的角度上思考问题**。 @@ -136,18 +136,26 @@ public: 每个加油站的剩余量rest[i]为gas[i] - cost[i]。 -i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,起始位置从i+1算起,再从0计算curSum。 +i从0开始累加rest[i],和记为curSum,一旦curSum小于零,说明[0, i]区间都不能作为起始位置,因为这个区间选择任何一个位置作为起点,到i这里都会断油,那么起始位置从i+1算起,再从0计算curSum。 如图: -![134.加油站](https://img-blog.csdnimg.cn/20201213162821958.png) -那么为什么一旦[i,j] 区间和为负数,起始位置就可以是j+1呢,j+1后面就不会出现更大的负数? +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230117165628.png) + +那么为什么一旦[0,i] 区间和为负数,起始位置就可以是i+1呢,i+1后面就不会出现更大的负数? + +如果出现更大的负数,就是更新i,那么起始位置又变成新的i+1了。 + +那有没有可能 [0,i] 区间 选某一个作为起点,累加到 i这里 curSum是不会小于零呢? 如图: -如果出现更大的负数,就是更新j,那么起始位置又变成新的j+1了。 +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230117170703.png) -而且j之前出现了多少负数,j后面就会出现多少正数,因为耗油总和是大于零的(前提我们已经确定了一定可以跑完全程)。 +如果 curSum<0 说明 区间和1 + 区间和2 < 0, 那么 假设从上图中的位置开始计数curSum不会小于0的话,就是 区间和2>0。 -**那么局部最优:当前累加rest[j]的和curSum一旦小于0,起始位置至少要是j+1,因为从j开始一定不行。全局最优:找到可以跑一圈的起始位置**。 +区间和1 + 区间和2 < 0 同时 区间和2>0,只能说明区间和1 < 0, 那么就会从假设的箭头初就开始从新选择其实位置了。 + + +**那么局部最优:当前累加rest[i]的和curSum一旦小于0,起始位置至少要是i+1,因为从i之前开始一定不行。全局最优:找到可以跑一圈的起始位置**。 局部最优可以推出全局最优,找不出反例,试试贪心! @@ -173,8 +181,8 @@ public: } }; ``` -* 时间复杂度:$O(n)$ -* 空间复杂度:$O(1)$ +* 时间复杂度:O(n) +* 空间复杂度:O(1) **说这种解法为贪心算法,才是有理有据的,因为全局最优解是根据局部最优推导出来的**。 diff --git "a/problems/0343.\346\225\264\346\225\260\346\213\206\345\210\206.md" "b/problems/0343.\346\225\264\346\225\260\346\213\206\345\210\206.md" index 25a6315497..bc69c5c450 100644 --- "a/problems/0343.\346\225\264\346\225\260\346\213\206\345\210\206.md" +++ "b/problems/0343.\346\225\264\346\225\260\346\213\206\345\210\206.md" @@ -21,6 +21,12 @@ * 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 * 说明: 你可以假设 n 不小于 2 且不大于 58。 + +# 算法公开课 + +**《代码随想录》算法视频公开课:[动态规划,本题关键在于理解递推公式!| LeetCode:343. 整数拆分](https://www.bilibili.com/video/BV1Mg411q7YJ/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + ## 思路 看到这道题目,都会想拆成两个呢,还是三个呢,还是四个.... diff --git "a/problems/0406.\346\240\271\346\215\256\350\272\253\351\253\230\351\207\215\345\273\272\351\230\237\345\210\227.md" "b/problems/0406.\346\240\271\346\215\256\350\272\253\351\253\230\351\207\215\345\273\272\351\230\237\345\210\227.md" index 934cc9eea2..73a7affc53 100644 --- "a/problems/0406.\346\240\271\346\215\256\350\272\253\351\253\230\351\207\215\345\273\272\351\230\237\345\210\227.md" +++ "b/problems/0406.\346\240\271\346\215\256\350\272\253\351\253\230\351\207\215\345\273\272\351\230\237\345\210\227.md" @@ -175,9 +175,8 @@ public: 对于写题解的同学,刷题用什么语言影响就非常大,如果自己语言没有学好而强调算法和编程语言没关系,其实是会误伤别人的。 -**这也是我为什么统一使用C++写题解的原因**,其实用其他语言java、python、php、go啥的,我也能写,我的Github上也有用这些语言写的小项目,但写题解的话,我就不能保证把语言特性这块讲清楚,所以我始终坚持使用最熟悉的C++写题解。 +**这也是我为什么统一使用C++写题解的原因** -**而且我在写题解的时候涉及语言特性,一般都会后面加上括号说明一下。没办法,认真负责就是我,哈哈**。 ## 其他语言版本 diff --git "a/problems/0417.\345\244\252\345\271\263\346\264\213\345\244\247\350\245\277\346\264\213\346\260\264\346\265\201\351\227\256\351\242\230.md" "b/problems/0417.\345\244\252\345\271\263\346\264\213\345\244\247\350\245\277\346\264\213\346\260\264\346\265\201\351\227\256\351\242\230.md" index 35fdc7d71b..f936399b44 100644 --- "a/problems/0417.\345\244\252\345\271\263\346\264\213\345\244\247\350\245\277\346\264\213\346\260\264\346\265\201\351\227\256\351\242\230.md" +++ "b/problems/0417.\345\244\252\345\271\263\346\264\213\345\244\247\350\245\277\346\264\213\346\260\264\346\265\201\351\227\256\351\242\230.md" @@ -3,10 +3,42 @@

参与本项目,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!

+ + + # 417. 太平洋大西洋水流问题 [题目链接](https://leetcode.cn/problems/pacific-atlantic-water-flow/) +有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。 + +这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度 。 + +岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。 + +返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋 。 + +示例 1: + +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230129103212.png) + +* 输入: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]] +* 输出: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]] + +示例 2: + +* 输入: heights = [[2,1],[1,2]] +* 输出: [[0,0],[0,1],[1,0],[1,1]] + +提示: + +* m == heights.length +* n == heights[r].length +* 1 <= m, n <= 200 +* 0 <= heights[r][c] <= 10^5 + + + ## 思路 不少同学可能被这道题的题目描述迷惑了,其实就是找到哪些点 可以同时到达太平洋和大西洋。 流动的方式只能从高往低流。 @@ -96,7 +128,7 @@ public: ## 优化 -那么我们可以 反过来想,从太平洋边上的节点 逆流而上,将遍历过的节点都标记上。 从大西洋的边上节点 逆流而长,讲遍历过的节点也标记上。 +那么我们可以 反过来想,从太平洋边上的节点 逆流而上,将遍历过的节点都标记上。 从大西洋的边上节点 逆流而长,将遍历过的节点也标记上。 然后两方都标记过的节点就是既可以流太平洋也可以流大西洋的节点。 从太平洋边上节点出发,如图: diff --git "a/problems/0435.\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264.md" "b/problems/0435.\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264.md" index 33f8c3f28f..e5675e04aa 100644 --- "a/problems/0435.\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264.md" +++ "b/problems/0435.\346\227\240\351\207\215\345\217\240\345\214\272\351\227\264.md" @@ -34,33 +34,21 @@ **相信很多同学看到这道题目都冥冥之中感觉要排序,但是究竟是按照右边界排序,还是按照左边界排序呢?** -这其实是一个难点! - -按照右边界排序,就要从左向右遍历,因为右边界越小越好,只要右边界越小,留给下一个区间的空间就越大,所以从左向右遍历,优先选右边界小的。 - -按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。 - -如果按照左边界排序,还从左向右遍历的话,其实也可以,逻辑会有所不同。 - -一些同学做这道题目可能真的去模拟去重复区间的行为,这是比较麻烦的,还要去删除区间。 - -题目只是要求移除区间的个数,没有必要去真实的模拟删除区间! +其实都可以。主要就是为了让区间尽可能的重叠。 **我来按照右边界排序,从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了**。 此时问题就是要求非交叉区间的最大个数。 -右边界排序之后,局部最优:优先选右边界小的区间,所以从左向右遍历,留给下一个区间的空间大一些,从而尽量避免交叉。全局最优:选取最多的非交叉区间。 - -局部最优推出全局最优,试试贪心! - 这里记录非交叉区间的个数还是有技巧的,如图: -![435.无重叠区间](https://img-blog.csdnimg.cn/20201221201553618.png) +![](https://code-thinking-1253855093.file.myqcloud.com/pics/20230201164134.png) 区间,1,2,3,4,5,6都按照右边界排好序。 -每次取非交叉区间的时候,都是可右边界最小的来做分割点(这样留给下一个区间的空间就越大),所以第一条分割线就是区间1结束的位置。 +当确定区间 1 和 区间2 重叠后,如何确定是否与 区间3 也重贴呢? + +就是取 区间1 和 区间2 右边界的最小值,因为这个最小值之前的部分一定是 区间1 和区间2 的重合部分,如果这个最小值也触达到区间3,那么说明 区间 1,2,3都是重合的。 接下来就是找大于区间1结束位置的区间,是从区间4开始。**那有同学问了为什么不从区间5开始?别忘了已经是按照右边界排序的了**。 @@ -97,28 +85,62 @@ public: 大家此时会发现如此复杂的一个问题,代码实现却这么简单! -## 总结 -本题我认为难度级别可以算是hard级别的! -总结如下难点: +## 补充(1) -* 难点一:一看题就有感觉需要排序,但究竟怎么排序,按左边界排还是右边界排。 -* 难点二:排完序之后如何遍历,如果没有分析好遍历顺序,那么排序就没有意义了。 -* 难点三:直接求重复的区间是复杂的,转而求最大非重复区间个数。 -* 难点四:求最大非重复区间个数时,需要一个分割点来做标记。 +左边界排序可不可以呢? -**这四个难点都不好想,但任何一个没想到位,这道题就解不了**。 +也是可以的,只不过 左边界排序我们就是直接求 重叠的区间,count为记录重叠区间数。 -一些录友可能看网上的题解代码很简单,照葫芦画瓢稀里糊涂的就过了,但是其题解可能并没有把问题难点讲清楚,然后自己再没有钻研的话,那么一道贪心经典区间问题就这么浪费掉了。 +```CPP +class Solution { +public: + static bool cmp (const vector& a, const vector& b) { + return a[0] < b[0]; // 改为左边界排序 + } + int eraseOverlapIntervals(vector>& intervals) { + if (intervals.size() == 0) return 0; + sort(intervals.begin(), intervals.end(), cmp); + int count = 0; // 注意这里从0开始,因为是记录重叠区间 + int end = intervals[0][1]; // 记录区间分割点 + for (int i = 1; i < intervals.size(); i++) { + if (intervals[i][0] >= end) end = intervals[i][1]; // 无重叠的情况 + else { // 重叠情况 + end = min(end, intervals[i][1]); + count++; + } + } + return count; + } +}; +``` -贪心就是这样,代码有时候很简单(不是指代码短,而是逻辑简单),但想法是真的难! +其实代码还可以精简一下, 用 intervals[i][1] 替代 end变量,只判断 重叠情况就好 -这和动态规划还不一样,动规的代码有个递推公式,可能就看不懂了,而贪心往往是直白的代码,但想法读不懂,哈哈。 +```CPP +class Solution { +public: + static bool cmp (const vector& a, const vector& b) { + return a[0] < b[0]; // 改为左边界排序 + } + int eraseOverlapIntervals(vector>& intervals) { + if (intervals.size() == 0) return 0; + sort(intervals.begin(), intervals.end(), cmp); + int count = 0; // 注意这里从0开始,因为是记录重叠区间 + for (int i = 1; i < intervals.size(); i++) { + if (intervals[i][0] < intervals[i - 1][1]) { //重叠情况 + intervals[i][1] = min(intervals[i - 1][1], intervals[i][1]); + count++; + } + } + return count; + } +}; -**所以我把本题的难点也一一列出,帮大家不仅代码看的懂,想法也理解的透彻!** +``` -## 补充 +## 补充(2) 本题其实和[452.用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)非常像,弓箭的数量就相当于是非交叉区间的数量,只要把弓箭那道题目代码里射爆气球的判断条件加个等号(认为[0,1][1,2]不是相邻区间),然后用总区间数减去弓箭数量 就是要移除的区间数量了。 @@ -129,7 +151,7 @@ class Solution { public: // 按照区间右边界排序 static bool cmp (const vector& a, const vector& b) { - return a[1] < b[1]; + return a[1] < b[1]; // 右边界排序 } int eraseOverlapIntervals(vector>& intervals) { if (intervals.size() == 0) return 0; @@ -149,13 +171,13 @@ public: }; ``` -这里按照 左区间遍历,或者按照右边界遍历,都可以AC,具体原因我还没有仔细看,后面有空再补充。 +这里按照 左边界排序,或者按照右边界排序,都可以AC,原理是一样的。 ```CPP class Solution { public: // 按照区间左边界排序 static bool cmp (const vector& a, const vector& b) { - return a[0] < b[0]; + return a[0] < b[0]; // 左边界排序 } int eraseOverlapIntervals(vector>& intervals) { if (intervals.size() == 0) return 0; diff --git "a/problems/0491.\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" "b/problems/0491.\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" index 82b3604eb9..6aa81539ec 100644 --- "a/problems/0491.\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" +++ "b/problems/0491.\351\200\222\345\242\236\345\255\220\345\272\217\345\210\227.md" @@ -23,11 +23,12 @@ * 数组中的整数范围是 [-100,100]。 * 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。 +# 算法公开课 -## 思路 +**《代码随想录》算法视频公开课:[回溯算法精讲,树层去重与树枝去重 | LeetCode:491.递增子序列](https://www.bilibili.com/video/BV1EG4y1h78v/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 -**如果对回溯算法基础还不了解的话,我还特意录制了一期视频:[带你学透回溯算法(理论篇)](https://www.bilibili.com/video/BV1cy4y167mM/)** 可以结合题解和视频一起看,希望对大家理解回溯算法有所帮助。 +## 思路 这个递增子序列比较像是取有序的子集。而且本题也要求不能有相同的递增子序列。 @@ -193,7 +194,6 @@ public: **对于养成思维定式或者套模板套嗨了的同学,这道题起到了很好的警醒作用。更重要的是拓展了大家的思路!** -**就酱,如果感觉「代码随想录」很干货,就帮Carl宣传一波吧!** ## 其他语言版本 diff --git "a/problems/0746.\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.md" "b/problems/0746.\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.md" index 3c6abd48f1..44b6406c8a 100644 --- "a/problems/0746.\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.md" +++ "b/problems/0746.\344\275\277\347\224\250\346\234\200\345\260\217\350\212\261\350\264\271\347\210\254\346\245\274\346\242\257.md" @@ -21,21 +21,28 @@ 示例 1: -输入:cost = [10, 15, 20] -输出:15 -解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。 - 示例 2: +* 输入:cost = [10, 15, 20] +* 输出:15 +* 解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。 -输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] -输出:6 -解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。 +示例 2: + +* 输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] +* 输出:6 +* 解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 ,跳过 cost[3] ,一共花费 6 。 提示: * cost 的长度范围是 [2, 1000]。 * cost[i] 将会是一个整型数据,范围为 [0, 999] 。 ------------------ + +# 算法公开课 + +**《代码随想录》算法视频公开课:[动态规划开更了!| LeetCode:746. 使用最小花费爬楼梯](https://www.bilibili.com/video/BV16G411c7yZ/),相信结合视频再看本篇题解,更有助于大家对本题的理解**。 + + +----------- 本题之前的题目描述是很模糊的,看不出来,第一步需要花费体力值,最后一步不用花费,还是说 第一步不花费体力值,最后一步花费。 @@ -194,8 +201,6 @@ public: 难的是把题目按梯度排好,循序渐进,再按照统一方法论把这些都串起来,所以大家不要催我哈,按照我的节奏一步一步来就行了。 -学算法,认准「代码随想录」,没毛病! - ## 其他语言版本 以下版本其他语言版本,大多是按照旧力扣题解来写的,欢迎大家在[Github](https://github.com/youngyangyang04/leetcode-master)上[提交pr](https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A),修正一波。