更新图床

This commit is contained in:
programmercarl
2023-03-10 14:02:32 +08:00
parent 2a9b627a90
commit 17cb4b45c7
134 changed files with 1169 additions and 829 deletions

View File

@ -254,5 +254,5 @@ traversal(cur->left, tmp, result);
* Github[leetcode-master](https://github.com/youngyangyang04/leetcode-master)
* 知乎:[代码随想录](https://www.zhihu.com/people/sun-xiu-yang-64)
![](https://img-blog.csdnimg.cn/2021013018121150.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/2021013018121150.png)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码.jpg width=450> </img></div>

View File

@ -1,5 +1,4 @@
# 本周小结!(回溯算法系列二)
> 例行每周小结
@ -32,7 +31,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
在[回溯算法:求组合总和(二)](https://programmercarl.com/0039.组合总和.html)第一个树形结构没有画出startIndex的作用**这里这里纠正一下,准确的树形结构如图所示:**
![39.组合总和](https://img-blog.csdnimg.cn/20201123202227835.png)
![39.组合总和](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123202227835.png)
## 周二
@ -46,7 +45,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
都知道组合问题可以抽象为树形结构,那么“使用过”在这个树形结构上是有两个维度的,一个维度是同一树枝上“使用过”,一个维度是同一树层上“使用过”。**没有理解这两个层面上的“使用过” 是造成大家没有彻底理解去重的根本原因**。
![40.组合总和II1](https://img-blog.csdnimg.cn/20201123202817973.png)
![40.组合总和II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123202817973.png)
我在图中将used的变化用橘黄色标注上可以看出在candidates[i] == candidates[i - 1]相同的情况下:
@ -80,7 +79,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
**本题的树形结构中,和代码的逻辑有一个小出入,已经判断不是回文的子串就不会进入递归了,纠正如下:**
![131.分割回文串](https://img-blog.csdnimg.cn/20201123203228309.png)
![131.分割回文串](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123203228309.png)
## 周四
@ -91,7 +90,7 @@ for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target;
树形图如下:
![93.复原IP地址](https://img-blog.csdnimg.cn/20201123203735933.png)
![93.复原IP地址](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123203735933-20230310133532452.png)
在本文的树形结构图中,我已经把详细的分析思路都画了出来,相信大家看了之后一定会思路清晰不少!
@ -113,7 +112,7 @@ if (s.size() > 12) return result; // 剪枝
如图:
![78.子集](https://img-blog.csdnimg.cn/202011232041348.png)
![78.子集](https://code-thinking-1253855093.file.myqcloud.com/pics/202011232041348.png)
认清这个本质之后,今天的题目就是一道模板题了。

View File

@ -1,5 +1,4 @@
# 本周小结!(回溯算法系列三)
## 周一
@ -12,14 +11,14 @@
树形结构如下:
![90.子集II](https://img-blog.csdnimg.cn/2020111217110449.png)
![90.子集II](https://code-thinking-1253855093.file.myqcloud.com/pics/2020111217110449-20230310133150714.png)
## 周二
在[回溯算法:递增子序列](https://programmercarl.com/0491.递增子序列.html)中,处处都能看到子集的身影,但处处是陷阱,值得好好琢磨琢磨!
树形结构如下:
![491. 递增子序列1](https://img-blog.csdnimg.cn/20201112170832333.png)
![491. 递增子序列1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201112170832333-20230310133155209.png)
[回溯算法:递增子序列](https://programmercarl.com/0491.递增子序列.html)留言区大家有很多疑问,主要还是和[回溯算法:求子集问题(二)](https://programmercarl.com/0090.子集II.html)混合在了一起。
@ -34,7 +33,7 @@
可以看出元素1在[1,2]中已经使用过了,但是在[2,1]中还要在使用一次1所以处理排列问题就不用使用startIndex了。
如图:
![46.全排列](https://img-blog.csdnimg.cn/20201112170304979.png)
![46.全排列](https://code-thinking-1253855093.file.myqcloud.com/pics/20201112170304979-20230310133201250.png)
**大家此时可以感受出排列问题的不同:**
@ -47,7 +46,7 @@
树形结构如下:
![47.全排列II1](https://img-blog.csdnimg.cn/20201112171930470.png)
![47.全排列II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201112171930470-20230310133206398.png)
**这道题目神奇的地方就是used[i - 1] == false也可以used[i - 1] == true也可以**
@ -55,11 +54,11 @@
树层上去重(used[i - 1] == false),的树形结构如下:
![47.全排列II2.png](https://img-blog.csdnimg.cn/20201112172230434.png)
![47.全排列II2.png](https://code-thinking-1253855093.file.myqcloud.com/pics/20201112172230434-20230310133211392.png)
树枝上去重used[i - 1] == true的树型结构如下
![47.全排列II3](https://img-blog.csdnimg.cn/20201112172327967.png)
![47.全排列II3](https://code-thinking-1253855093.file.myqcloud.com/pics/20201112172327967-20230310133216389.png)
**可以清晰的看到使用(used[i - 1] == false),即树层去重,效率更高!**
@ -72,14 +71,17 @@
**所以这块就说一说我个人理解,对内容持开放态度,集思广益,欢迎大家来讨论!**
子集问题分析:
* 时间复杂度:$O(n × 2^n)$,因为每一个元素的状态无外乎取与不取,所以时间复杂度为$O(2^n)$,构造每一组子集都需要填进数组,又有需要$O(n)$,最终时间复杂度:$O(n × 2^n)$。
* 空间复杂度:$O(n)$递归深度为n所以系统栈所用空间为$O(n)$每一层递归所用的空间都是常数级别注意代码里的result和path都是全局变量就算是放在参数里传的也是引用并不会新申请内存空间最终空间复杂度为$O(n)$。
排列问题分析:
* 时间复杂度:$O(n!)$这个可以从排列的树形图中很明显发现每一层节点为n第二层每一个分支都延伸了n-1个分支再往下又是n-2个分支所以一直到叶子节点一共就是 n * n-1 * n-2 * ..... 1 = n!。每个叶子节点都会有一个构造全排列填进数组的操作(对应的代码:`result.push_back(path)`),该操作的复杂度为$O(n)$。所以最终时间复杂度为n * n!,简化为$O(n!)$。
* 空间复杂度:$O(n)$,和子集问题同理。
组合问题分析:
* 时间复杂度:$O(n × 2^n)$,组合问题其实就是一种子集的问题,所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
* 空间复杂度:$O(n)$,和子集问题同理。

View File

@ -1,5 +1,4 @@
# 本周小结!(贪心算法系列二)
## 周一
@ -16,7 +15,7 @@
如图:
![122.买卖股票的最佳时机II](https://img-blog.csdnimg.cn/2020112917480858.png)
![122.买卖股票的最佳时机II](https://code-thinking-1253855093.file.myqcloud.com/pics/2020112917480858.png)
## 周二
@ -32,7 +31,7 @@
如图:
![55.跳跃游戏](https://img-blog.csdnimg.cn/20201124154758229.png)
![55.跳跃游戏](https://code-thinking-1253855093.file.myqcloud.com/pics/20201124154758229.png)
## 周三
@ -45,7 +44,7 @@
如图:
![45.跳跃游戏II](https://img-blog.csdnimg.cn/20201201232309103.png)
![45.跳跃游戏II](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232309103-20230310133110942.png)
注意:**图中的移动下标是到当前这步覆盖的最远距离下标2的位置此时没有到终点只能增加第二步来扩大覆盖范围**。
@ -56,10 +55,10 @@
而版本二就比较统一的,超过范围,步数就加一,但在移动下标的范围了做了文章。
即如果覆盖最远距离下标是倒数第二点:直接加一就行,默认一定可以到终点。如图:
![45.跳跃游戏II2](https://img-blog.csdnimg.cn/20201201232445286.png)
![45.跳跃游戏II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232445286-20230310133115650.png)
如果覆盖最远距离下标不是倒数第二点,说明本次覆盖已经到终点了。如图:
![45.跳跃游戏II1](https://img-blog.csdnimg.cn/20201201232338693.png)
![45.跳跃游戏II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201201232338693-20230310133120115.png)
有的录友认为版本一好理解,有的录友认为版本二好理解,其实掌握一种就可以了,也不用非要比拼一下代码的简洁性,简洁程度都差不多了。

View File

@ -76,7 +76,8 @@
文中从计算机硬件出发,分析计算机的计算性能,然后亲自做实验,整理出数据如下:
![程序超时1](https://img-blog.csdnimg.cn/20201208231559175.png)
![程序超时1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201208231559175-20230310133304038.png)
**大家有一个数量级上的概念就可以了!**

View File

@ -37,7 +37,8 @@
先贪心一边,局部最优:只要右边评分比左边大,右边的孩子就多一个糖果,全局最优:相邻的孩子中,评分高的右孩子获得比左边孩子更多的糖果
如图:
![135.分发糖果](https://img-blog.csdnimg.cn/20201117114916878.png)
![135.分发糖果](https://code-thinking-1253855093.file.myqcloud.com/pics/20201117114916878-20230310133332759.png)
接着在贪心另一边,左孩子大于右孩子,左孩子的糖果就要比右孩子多。
@ -49,7 +50,7 @@
局部最优可以推出全局最优。
如图:
![135.分发糖果1](https://img-blog.csdnimg.cn/20201117115658791.png)
![135.分发糖果1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201117115658791-20230310133346127.png)
## 周三

View File

@ -1,5 +1,4 @@
# 本周小结!(贪心算法系列四)
## 周一
@ -10,7 +9,7 @@
如图:
![452.用最少数量的箭引爆气球](https://img-blog.csdnimg.cn/20201123101929791.png)
![452.用最少数量的箭引爆气球](https://code-thinking-1253855093.file.myqcloud.com/pics/20201123101929791-20230310133845522.png)
模拟射气球的过程,很多同学真的要去模拟了,实时把气球从数组中移走,这么写的话就复杂了,从前向后遍历重复的只要跳过就可以的。
@ -22,7 +21,7 @@
如图:
![435.无重叠区间](https://img-blog.csdnimg.cn/20201221201553618.png)
![435.无重叠区间](https://code-thinking-1253855093.file.myqcloud.com/pics/20201221201553618.png)
细心的同学就发现了,此题和 [贪心算法:用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)非常像。
@ -31,6 +30,7 @@
把[贪心算法:用最少数量的箭引爆气球](https://programmercarl.com/0452.用最少数量的箭引爆气球.html)代码稍做修改就可以AC本题。
修改后的C++代码如下:
```CPP
class Solution {
public:
@ -71,7 +71,7 @@ public:
如图:
![763.划分字母区间](https://img-blog.csdnimg.cn/20201222191924417.png)
![763.划分字母区间](https://code-thinking-1253855093.file.myqcloud.com/pics/20201222191924417-20230310133855435.png)
## 周四
@ -86,7 +86,7 @@ public:
如图
![56.合并区间](https://img-blog.csdnimg.cn/20201223200632791.png)
![56.合并区间](https://code-thinking-1253855093.file.myqcloud.com/pics/20201223200632791-20230310133859587.png)
## 总结

View File

@ -30,7 +30,7 @@ for (int i = 1; i < m; i++) {
}
```
![62.不同路径1](https://img-blog.csdnimg.cn/20201209113631392.png)
![62.不同路径1](https://code-thinking-1253855093.file.myqcloud.com/pics/20201209113631392-20230310133703294.png)
## 周二
@ -44,7 +44,7 @@ dp[i][j]定义依然是表示从0 0出发到(i, j) 有dp[i][j]条
如图:
![63.不同路径II](https://img-blog.csdnimg.cn/20210104114513928.png)
![63.不同路径II](https://code-thinking-1253855093.file.myqcloud.com/pics/20210104114513928-20230310133707783.png)
这里难住了不少同学,代码如下:
@ -69,11 +69,11 @@ for (int i = 1; i < m; i++) {
拿示例1来举例如题
![63.不同路径II1](https://img-blog.csdnimg.cn/20210104114548983.png)
![63.不同路径II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210104114548983-20230310133711888.png)
对应的dp table 如图:
![63.不同路径II2](https://img-blog.csdnimg.cn/20210104114610256.png)
![63.不同路径II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20210104114610256-20230310133715981.png)
## 周三
@ -107,9 +107,10 @@ for (int i = 3; i <= n ; i++) {
}
}
```
举例当n为10 的时候dp数组里的数值如下
![343.整数拆分](https://img-blog.csdnimg.cn/20210104173021581.png)
![343.整数拆分](https://code-thinking-1253855093.file.myqcloud.com/pics/20210104173021581-20230310133720552.png)
@ -141,7 +142,7 @@ dp数组如何初始化只需要初始化dp[0]就可以了,推导的基础
n为5时候的dp数组状态如图
![96.不同的二叉搜索树3](https://img-blog.csdnimg.cn/20210107093253987.png)
![96.不同的二叉搜索树3](https://code-thinking-1253855093.file.myqcloud.com/pics/20210107093253987-20230310133724531.png)
## 总结

View File

@ -1,4 +1,6 @@
# 本周小结!(动态规划系列三)
本周我们正式开始讲解背包问题,也是动规里非常重要的一类问题。
背包问题其实有很多细节,如果了解个大概,然后也能一气呵成把代码写出来,但稍稍变变花样可能会陷入迷茫了。
@ -15,7 +17,7 @@
关于其他几种常用的背包,大家看这张图就了然于胸了:
![416.分割等和子集1](https://img-blog.csdnimg.cn/20210117171307407.png)
![416.分割等和子集1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210117171307407-20230310133624872.png)
本文用动规五部曲详细讲解了01背包的二维dp数组的实现方法大家其实可以发现最简单的是推导公式了推导公式估计看一遍就记下来了但难就难在确定初始化和遍历顺序上。
@ -61,14 +63,14 @@ for(int i = 1; i < weight.size(); i++) { // 遍历物品
物品为:
| | 重量 | 价值 |
| --- | --- | --- |
| ----- | ---- | ---- |
| 物品0 | 1 | 15 |
| 物品1 | 3 | 20 |
| 物品2 | 4 | 30 |
来看一下对应的dp数组的数值如图
![动态规划-背包问题4](https://img-blog.csdnimg.cn/20210118163425129.jpg)
![动态规划-背包问题4](https://code-thinking-1253855093.file.myqcloud.com/pics/20210118163425129-20230310133630224.jpg)
最终结果就是dp[2][4]。
@ -120,7 +122,7 @@ for(int i = 0; i < weight.size(); i++) { // 遍历物品
一维dp分别用物品0物品1物品2 来遍历背包,最终得到结果如下:
![动态规划-背包问题9](https://img-blog.csdnimg.cn/20210110103614769.png)
![动态规划-背包问题9](https://code-thinking-1253855093.file.myqcloud.com/pics/20210110103614769-20230310133634873.png)
## 周三

View File

@ -35,7 +35,7 @@ bagSize = (S + sum) / 2 = (3 + 5) / 2 = 4
dp数组状态变化如下
![494.目标和](https://img-blog.csdnimg.cn/20210125120743274.jpg)
![494.目标和](https://code-thinking-1253855093.file.myqcloud.com/pics/20210125120743274-20230310132918821.jpg)
## 周二
@ -72,7 +72,8 @@ dp[i][j] = max(dp[i][j], dp[i - zeroNum][j - oneNum] + 1);
最后dp数组的状态如下所示
![474.一和零](https://img-blog.csdnimg.cn/20210120111201512.jpg)
![474.一和零](https://code-thinking-1253855093.file.myqcloud.com/pics/20210120111201512-20230310132936011.jpg)
## 周三

View File

@ -29,7 +29,7 @@ dp[1] = max(nums[0], nums[1]);
以示例二,输入[2,7,9,3,1]为例。
![198.打家劫舍](https://img-blog.csdnimg.cn/20210221170954115.jpg)
![198.打家劫舍](https://code-thinking-1253855093.file.myqcloud.com/pics/20210221170954115-20230310133425353.jpg)
红框dp[nums.size() - 1]为结果。
@ -41,15 +41,15 @@ dp[1] = max(nums[0], nums[1]);
* 情况一:考虑不包含首尾元素
![213.打家劫舍II](https://img-blog.csdnimg.cn/20210129160748643.jpg)
![213.打家劫舍II](https://code-thinking-1253855093.file.myqcloud.com/pics/20210129160748643.jpg)
* 情况二:考虑包含首元素,不包含尾元素
![213.打家劫舍II1](https://img-blog.csdnimg.cn/20210129160821374.jpg)
![213.打家劫舍II1](https://code-thinking-1253855093.file.myqcloud.com/pics/20210129160821374.jpg)
* 情况三:考虑包含尾元素,不包含首元素
![213.打家劫舍II2](https://img-blog.csdnimg.cn/20210129160842491.jpg)
![213.打家劫舍II2](https://code-thinking-1253855093.file.myqcloud.com/pics/20210129160842491.jpg)
需要注意的是,**“考虑” 不等于 “偷”**例如情况三虽然是考虑包含尾元素但不一定要选尾部元素对于情况三取nums[1] 和 nums[3]就是最大的。
@ -135,9 +135,11 @@ dp数组含义下标为0记录不偷该节点所得到的的最大金钱
2. 确定终止条件
在遍历的过程中如果遇到空间点的话很明显无论偷还是不偷都是0所以就返回
```
if (cur == NULL) return vector<int>{0, 0};
```
3. 确定遍历顺序
采用后序遍历,代码如下:
@ -175,7 +177,7 @@ return {val2, val1};
以示例1为例dp数组状态如下**注意用后序遍历的方式推导**
![337.打家劫舍III](https://img-blog.csdnimg.cn/20210129181331613.jpg)
![337.打家劫舍III](https://code-thinking-1253855093.file.myqcloud.com/pics/20210129181331613.jpg)
**最后头结点就是 取下标0 和 下标1的最大值就是偷得的最大金钱**
@ -196,6 +198,7 @@ return {val2, val1};
这里我给出了三种解法:
暴力解法代码:
```CPP
class Solution {
public:

View File

@ -76,7 +76,7 @@ dp[0][4] = 0;
以输入[1,2,3,4,5]为例
![123.买卖股票的最佳时机III](https://img-blog.csdnimg.cn/20201228181724295.png)
![123.买卖股票的最佳时机III](https://code-thinking-1253855093.file.myqcloud.com/pics/20201228181724295.png)
可以看到红色框为最后两次卖出的状态。
@ -142,7 +142,8 @@ for (int j = 1; j < 2 * k; j += 2) {
以输入[1,2,3,4,5]k=2为例。
![188.买卖股票的最佳时机IV](https://img-blog.csdnimg.cn/20201229100358221.png)
![188.买卖股票的最佳时机IV](https://code-thinking-1253855093.file.myqcloud.com/pics/20201229100358221-20230310133805763.png)
最后一次卖出一定是利润最大的dp[prices.size() - 1][2 * k]即红色部分就是最后求解。
@ -194,7 +195,8 @@ vector<vector<int>> dp(n, vector<int>(3, 0));
以 [1,2,3,0,2] 为例dp数组如下
![309.最佳买卖股票时机含冷冻期](https://img-blog.csdnimg.cn/20201229163725348.png)
![309.最佳买卖股票时机含冷冻期](https://code-thinking-1253855093.file.myqcloud.com/pics/20201229163725348.png)
最后两个状态 不持有股票(能购买) 和 不持有股票(冷冻期)都有可能最后结果,取最大的。