Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit7343c01

Browse files
Update
1 parent4c2a526 commit7343c01

11 files changed

+371
-46
lines changed

‎README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@
123123
*[二叉树:搜索树中的删除操作](https://mp.weixin.qq.com/s/-p-Txvch1FFk3ygKLjPAKw)
124124
*[二叉树:修剪一棵搜索树](https://mp.weixin.qq.com/s/QzmGfYUMUWGkbRj7-ozHoQ)
125125
*[二叉树:构造一棵搜索树](https://mp.weixin.qq.com/s/sy3ygnouaZVJs8lhFgl9mw)
126-
126+
*[二叉树:搜索树转成累加树](https://mp.weixin.qq.com/s/hZtJh4T5lIGBarY-lZJf6Q)
127+
*[二叉树:总结篇!(需要掌握的二叉树技能都在这里了)](https://mp.weixin.qq.com/s/-ZJn3jJVdF683ap90yIj4Q)
127128

128129

129130
(持续更新中....)

‎pics/77.组合1.png

122 KB
Loading

‎pics/77.组合2.png

110 KB
Loading

‎pics/77.组合3.png

109 KB
Loading

‎problems/0053.最大子序和.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
>笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,更多原创技术文章欢迎关注「代码随想录」,校招社招求职内推欢迎通过公众号「代码随想录」联系我,度厂很缺人!
2-
3-
>笔者在BAT从事技术研发多年,利用工作之余重刷leetcode,希望结合自己多年的实践经验,把算法讲的更清楚,更多原创文章欢迎关注公众号「代码随想录」。
41

52
##题目地址
63
https://leetcode-cn.com/problems/maximum-subarray/

‎problems/0077.组合.md

Lines changed: 95 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
12
#第77题. 组合
3+
4+
题目链接:https://leetcode-cn.com/problems/combinations/
5+
26
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
37
示例:
48

@@ -13,6 +17,7 @@
1317
[1,4],
1418
]
1519

20+
1621
#思路
1722

1823
这是回溯法的经典题目。
@@ -33,65 +38,122 @@
3338
那么就三层for循环,代码如下:
3439

3540
```
36-
for (int i = 1; i <= n; i++) {
37-
for (int j = i + 1; j <= n; j++) {
38-
for (int u = j + 1; u <=n; n++) {
41+
for (int i = 1; i <= n; i++) {
42+
for (int j = i + 1; j <= n; j++) {
43+
for (int u = j + 1; u <=n; n++) {
3944
40-
}
4145
}
4246
}
47+
}
4348
```
44-
**如果n为 100,k为50呢,那就50层for循环,是不是开始窒息。**
49+
50+
**如果n为100,k为50呢,那就50层for循环,是不是开始窒息**
4551

4652
那么回溯法就能解决这个问题了。
4753

48-
回溯是用来做选择的,递归用来节点层叠嵌套**每一次的递归是层叠嵌套的关系,可以用于解决多层嵌套循环的问题。**
54+
回溯是用来做选择,递归用来做节点层叠嵌套(可以理解是随便开K的for循环)**每一次的递归相当于嵌套一个for循环,可以用于解决多层嵌套循环的问题了**
4955

50-
其实子集和组合问题都可以抽象为一个树形结构,如下:
56+
**回溯问题都可以抽象为一棵树形结构!用树形结构来理解回溯就容易多了**
5157

58+
那么我们把组合问题抽象为如下树形结构:
5259

5360
<imgsrc='../pics/77.组合.png'width=600> </img></div>
5461

55-
可以看一下这个棵树,一开始集合是 1,2,3,4, 从左向右去数,取过的数,不在重复取。
62+
可以看出这个棵树,一开始集合是 1,2,3,4, 从左向右取数,取过的数,不在重复取。
63+
64+
第一次取1,集合变为2,3,4 ,因为k为2,我们只需要再取一个数就可以了,分别取,2,3,4, 得到集合[1,2][1,3][1,4],以此类推。
65+
66+
**回溯的问题都可以抽象为一个树形结构,在求解组合问题的过程中,n相当于树的宽度,k相当于树的深度**
67+
68+
**每次从集合中选组元素,可选择的范围随着选择的进行而限缩,调整可选择的范围**
69+
70+
如何在这个树上遍历,然后收集到我们要的结果集呢?
5671

57-
第一取1,集合变为2,3,4 ,因为k为2,我们只需要去一个数就可以了,分别取,2,3,4, 得到集合[1,2][1,3][1,4],以此类推
72+
用的就是回溯搜索法,**可以发现,每次搜索到了叶子节点,我们就找到了一个结果**
5873

59-
**其实这就转化成从集合中选取子集的问题,可选择的范围随着选择的进行而限缩,于是做剪枝,调整可选择的范围**
6074

61-
如何在这个树上遍历,然后收集到我们要的结果集呢,用的就是回溯搜索法,**可以发现,每次搜索到了叶子节点,我们就找到了一个结果。**
75+
**这份模板,大家可以要记住了,后面做回溯搜索的题目,都离不开这个模板**
76+
77+
##求组合
78+
79+
掌握了模板之后,我们再来看一下这道求组合的题目。
80+
81+
* 回溯函数返回值以及参数
82+
83+
在这里要定义两个全局变量,一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。
84+
85+
代码如下:
6286

63-
分析完过程,我们来看一下 回溯算法的模板框架如下:
6487
```
65-
backtracking() {
66-
if (终止条件) {
67-
存放结果;
68-
}
88+
vector<vector<int>> result; // 存放符合条件结果的集合
89+
vector<int> path; // 用来存放符合条件结果
90+
```
6991

70-
for (选择:选择列表(可以想成树中节点孩子的数量)) {
71-
递归,处理节点;
72-
backtracking();
73-
回溯,撤销处理结果
74-
}
92+
其实不定义这两个全局遍历也是可以的,把这两个变量放进回溯函数的参数里,但为了函数里参数太多影响可读性,所以我定义全局变量。
93+
94+
首先两个参数,集合n里面取k的数,是两个int型的变量。
95+
96+
然后还需要一个参数,也为int型变量startIndex,这个参数用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。
97+
98+
为什么要有这个startIndex呢?
99+
100+
从下图中红线部分可以看出,在集合[1,2,3,4]取1之后,下一层递归,就要在[2,3,4]中取数了,那么下一层递归如何知道从[2,3,4]中取数呢,靠的就是startIndex。
101+
102+
<imgsrc='../pics/77.组合2.png'width=600> </img></div>
103+
104+
所以需要startIndex来记录下一层递归,搜索的起始位置。
105+
106+
那么整体代码如下:
107+
108+
```
109+
vector<vector<int>> result; // 存放符合条件结果的集合
110+
vector<int> path; // 用来存放符合条件单一结果
111+
void backtracking(int n, int k, int startIndex)
112+
```
113+
114+
* 回溯函数终止条件
115+
116+
什么时候到达所谓的叶子节点了呢?
117+
118+
就是path这个数组的大小如果达到k,说明我们找到了一个集合大小为k的组合了,在图中path存的就是根节点到叶子节点的路径。
119+
120+
如图红色部分:
121+
122+
<imgsrc='../pics/77.组合3.png'width=600> </img></div>
123+
124+
此时用result二维数组,把path保存起来,并终止本层递归。
125+
126+
所以终止条件代码如下:
127+
128+
```
129+
if (path.size() == k) {
130+
result.push_back(path);
131+
return;
75132
}
76133
```
77134

78-
分析模板:
79135

80-
什么是达到了终止条件,树中就可以看出,搜到了叶子节点了,就找到了一个符合题目要求的答案,就把这个答案存放起来。
136+
* 回溯搜索的遍历过程
81137

82-
看一下这个for循环,这个for循环是做什么的,for 就是处理树中节点各个孩子的情况, 一个节点有多少个孩子,这个for循环就执行多少次。
138+
在如下如中,我们知道for循环用来横向遍历,递归的过程是纵向遍历。
139+
<imgsrc='../pics/77.组合1.png'width=600> </img></div>
83140

84-
最后就要看这个递归的过程了,注意这个backtracking就是自己调用自己,实现递归
141+
如此我们才遍历完图中的这棵树
85142

86-
一些同学对递归操作本来就不熟练,递归上面又加上一个for循环,可能就更迷糊了, 我来给大家捋顺一下。
143+
那么for循环每次就是从startIndex开始遍历,然后用path保存每次遍历到的节点。
87144

88-
这个backtracking 其实就是向树的叶子节点方向遍历, for循环可以理解是横向遍历,backtracking 就是纵向遍历,这样就把这棵树全遍历完了。
145+
代码如下:
89146

90-
那么backtracking就是一直往深处遍历,总会遇到叶子节点,遇到了叶子节点,就要返回,那么backtracking的下面部分就是回溯的操作了,撤销本次处理的结果。
147+
```
148+
for (int i = startIndex; i <= n; i++) {
149+
path.push_back(i); // 处理节点
150+
backtracking(n, k, i + 1); // 注意下一层搜索要从i+1开始
151+
path.pop_back(); // 回溯,撤销处理的节点
152+
}
153+
```
91154

92-
分析完模板,本题代码如下
155+
关键地方都讲完了,组合问题C++完整代码如下
93156

94-
#C++ 代码
95157

96158
```
97159
class Solution {
@@ -103,16 +165,16 @@ private:
103165
result.push_back(path);
104166
return;
105167
}
106-
// 这个for循环有讲究,组合的时候 要用startIndex,排列的时候就要从0开始
107168
for (int i = startIndex; i <= n; i++) {
108169
path.push_back(i); // 处理节点
109170
backtracking(n, k, i + 1);
110171
path.pop_back(); // 回溯,撤销处理的节点
111172
}
112173
}
113174
public:
114-
115175
vector<vector<int>> combine(int n, int k) {
176+
result.clear(); // 可以不写
177+
path.clear(); // 可以不写
116178
backtracking(n, k, 1);
117179
return result;
118180
}
@@ -145,7 +207,7 @@ for (int i = startIndex; i <= n; i++)
145207
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++)
146208
```
147209

148-
整体代码如下
210+
优化后整体代码如下
149211

150212
```
151213
class Solution {
@@ -157,7 +219,6 @@ private:
157219
result.push_back(path);
158220
return;
159221
}
160-
// 这个for循环有讲究,组合的时候 要用startIndex,排列的时候就要从0开始
161222
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {
162223
path.push_back(i); // 处理节点
163224
backtracking(n, k, i + 1);
@@ -174,6 +235,3 @@ public:
174235
```
175236

176237

177-
178-
#观后感
179-
我来写一下观后感: 很厉害,转化成从集合中选取子集的问题,可选择的范围随着选择的进行而限缩,于是做剪枝,调整可选择的范围。 每一次的递归是层叠嵌套的关系,可以用于解决多层嵌套循环的问题。 每一层递归中,尽量节省循环次数,这样在后续的递归调用中,节省下来的循环会被以至少指数等级放大。

‎problems/0104.二叉树的最大深度.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,12 +174,13 @@ public:
174174
int depth = 0;
175175
while (!que.empty()) {
176176
int size = que.size();
177+
vector<int> vec;
177178
depth++; // 记录深度
178179
for (int i = 0; i < size; i++) {
179180
Node* node = que.front();
180181
que.pop();
181-
for (inti = 0;i < node->children.size();i++) {
182-
if (node->children[i]) que.push(node->children[i]);
182+
for (intj = 0;j < node->children.size();j++) {
183+
if (node->children[j]) que.push(node->children[j]);
183184
}
184185
}
185186
}

‎problems/0559.N叉树的最大深度.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ public:
3131
while (!que.empty()) {
3232
int size = que.size();
3333
vector<int> vec;
34-
depth++;
34+
depth++; // 记录深度
3535
for (int i = 0; i < size; i++) {
3636
Node* node = que.front();
3737
que.pop();
38-
for (inti = 0;i < node->children.size();i++) {
39-
if (node->children[i]) que.push(node->children[i]);
38+
for (intj = 0;j < node->children.size();j++) {
39+
if (node->children[j]) que.push(node->children[j]);
4040
}
4141
}
4242
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp