Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 867af23

Browse files
Update
1 parent 5781e5e commit 867af23

7 files changed

+187
-17
lines changed

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
* 求职
2121
* [程序员应该如何写简历(附简历模板)](https://mp.weixin.qq.com/s/PkBpde0PV65dJjj9zZJYtg)
22-
* [一线互联网公司技术面试的流程以及注意事项](https://mp.weixin.qq.com/s/1VMvQ_6HbVpEn85CNilTiw)
22+
* [BAT级别技术面试流程和注意事项都在这里了](https://mp.weixin.qq.com/s/815qCyFGVIxwut9I_7PNFw)
2323

2424
* 算法性能分析
2525
* [究竟什么是时间复杂度,怎么求时间复杂度,看这一篇就够了](https://mp.weixin.qq.com/s/lYL9TSxLqCeFXIdjt4dcIw)
@@ -128,6 +128,8 @@
128128

129129
* 回溯算法
130130
* [关于回溯算法,你该了解这些!](https://mp.weixin.qq.com/s/gjSgJbNbd1eAA5WkA-HeWw)
131+
* [回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)
132+
* [回溯算法:组合问题再剪剪枝](https://mp.weixin.qq.com/s/Ri7spcJMUmph4c6XjPWXQA)
131133

132134
(持续更新中....)
133135

@@ -351,6 +353,7 @@
351353
|[0239.滑动窗口最大值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0239.滑动窗口最大值.md) |滑动窗口/队列 |困难| **单调队列**|
352354
|[0242.有效的字母异位词](https://github.com/youngyangyang04/leetcode/blob/master/problems/0242.有效的字母异位词.md) |哈希表 |简单| **哈希**|
353355
|[0257.二叉树的所有路径](https://github.com/youngyangyang04/leetcode/blob/master/problems/0257.二叉树的所有路径.md) ||简单| **递归/回溯**|
356+
|[0316.去除重复字母](https://github.com/youngyangyang04/leetcode/blob/master/problems/0316.去除重复字母.md) |贪心/字符串 |中等| **单调栈** 这道题目处理的情况比较多,属于单调栈中的难题|
354357
|[0332.重新安排行程](https://github.com/youngyangyang04/leetcode/blob/master/problems/0332.重新安排行程.md) |深度优先搜索/回溯 |中等| **深度优先搜索/回溯算法**|
355358
|[0344.反转字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0344.反转字符串.md) |字符串 |简单| **双指针**|
356359
|[0347.前K个高频元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0347.前K个高频元素.md) |哈希/堆/优先级队列 |中等| **哈希/优先级队列**|
@@ -396,6 +399,7 @@
396399
|[0977.有序数组的平方](https://github.com/youngyangyang04/leetcode/blob/master/problems/0977.有序数组的平方.md) |数组 |中等|**双指针** 还是比较巧妙的|
397400
|[1002.查找常用字符](https://github.com/youngyangyang04/leetcode/blob/master/problems/1002.查找常用字符.md) ||简单|****|
398401
|[1047.删除字符串中的所有相邻重复项](https://github.com/youngyangyang04/leetcode/blob/master/problems/1047.删除字符串中的所有相邻重复项.md) |哈希表 |简单|**哈希表/数组**|
402+
|[1207.独一无二的出现次数](https://github.com/youngyangyang04/leetcode/blob/master/problems/1207.独一无二的出现次数.md) |哈希表 |简单|**哈希** 两层哈希|
399403
|[1365.有多少小于当前数字的数字](https://github.com/youngyangyang04/leetcode/blob/master/problems/1365.有多少小于当前数字的数字.md) |数组、哈希表 |简单|**哈希** 从后遍历的技巧很不错|
400404
|[1382.将二叉搜索树变平衡](https://github.com/youngyangyang04/leetcode/blob/master/problems/1047.删除字符串中的所有相邻重复项.md) |二叉搜索树 |中等|**递归** **迭代** 98和108的组合题目|
401405
|[剑指Offer05.替换空格](https://github.com/youngyangyang04/leetcode/blob/master/problems/剑指Offer05.替换空格.md) |字符串 |简单|**双指针**|
75.5 KB
Loading

pics/77.组合4.png

190 KB
Loading

problems/0077.组合优化.md

Lines changed: 83 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,111 @@
11

2+
> 如果想在电脑上看文章的话,可以看这里:https://github.com/youngyangyang04/leetcode-master,已经按照顺序整理了「代码随想录」的所有文章,可以fork到自己仓库里,随时复习。**那么重点来了,来都来了,顺便给一个star吧,哈哈**
3+
4+
5+
[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)中,我们通过回溯搜索法,解决了n个数中求k个数的组合问题。
6+
7+
文中的回溯法是可以剪枝优化的,本篇我们继续来看一下题目77. 组合。
8+
9+
链接:https://leetcode-cn.com/problems/combinations/
10+
11+
**看本篇之前,需要先看[回溯算法:求组合问题!](https://mp.weixin.qq.com/s/OnBjbLzuipWz_u4QfmgcqQ)**
12+
13+
大家先回忆一下[77. 组合]给出的回溯法的代码:
14+
15+
```
16+
class Solution {
17+
private:
18+
vector<vector<int>> result; // 存放符合条件结果的集合
19+
vector<int> path; // 用来存放符合条件结果
20+
void backtracking(int n, int k, int startIndex) {
21+
if (path.size() == k) {
22+
result.push_back(path);
23+
return;
24+
}
25+
for (int i = startIndex; i <= n; i++) {
26+
path.push_back(i); // 处理节点
27+
backtracking(n, k, i + 1); // 递归
28+
path.pop_back(); // 回溯,撤销处理的节点
29+
}
30+
}
31+
public:
32+
vector<vector<int>> combine(int n, int k) {
33+
result.clear(); // 可以不写
34+
path.clear(); // 可以不写
35+
backtracking(n, k, 1);
36+
return result;
37+
}
38+
};
39+
```
40+
241
## 剪枝优化
342

443
我们说过,回溯法虽然是暴力搜索,但也有时候可以有点剪枝优化一下的。
544

6-
在遍历的过程中有如下代码
45+
在遍历的过程中有如下代码:
746

847
```
9-
for (int i = startIndex; i <= n; i++)
48+
for (int i = startIndex; i <= n; i++) {
49+
path.push_back(i);
50+
backtracking(n, k, i + 1);
51+
path.pop_back();
52+
}
1053
```
1154

1255
这个遍历的范围是可以剪枝优化的,怎么优化呢?
1356

14-
来举一个例子,n = 4, k = 4的话,那么从2开始的遍历都没有意义了。
57+
来举一个例子,n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了。
58+
59+
这么说有点抽象,如图所示:
60+
61+
<img src='../pics/77.组合4.png' width=600> </img></div>
62+
63+
图中每一个节点(图中为矩形),就代表本层的一个for循环,那么每一层的for循环从第二个数开始遍历的话,都没有意义,都是无效遍历。
64+
65+
**所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置**
66+
67+
**如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了**
1568

16-
所以,可以优化递归中每一层中for循环搜索的起始位置。
69+
注意代码中i,就是for循环里选择的起始位置。
70+
```
71+
for (int i = startIndex; i <= n; i++) {
72+
```
1773

18-
优化过程如下
74+
接下来看一下优化过程如下
1975

2076
1. 已经选择的元素个数:path.size();
2177

22-
2. 要选择的元素个数 : k - path.size();
78+
2. 还需要的元素个数为: k - path.size();
79+
80+
3. 在集合n中至少要从该起始位置 : n - (k - path.size()) + 1,开始遍历
2381

24-
3. 在集合n中开始选择的起始位置 : n - (k - path.size());
82+
为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。
2583

26-
因为起始位置是从1开始的,而且代码里是n <= 起始位置,所以 集合n中开始选择的起始位置 : n - (k - path.size()) + 1;
84+
举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。
2785

28-
所以优化之后是:
86+
从2开始搜索都是合理的,可以是组合[2, 3, 4]
87+
88+
这里大家想不懂的话,建议也举一个例子,就知道是不是要+1了。
89+
90+
所以优化之后的for循环是:
2991

3092
```
31-
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++)
93+
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置
3294
```
3395

3496
优化后整体代码如下:
3597

3698
```
3799
class Solution {
38100
private:
39-
vector<vector<int>> result; // 存放符合条件结果的集合
40-
vector<int> path; // 用来存放符合条件结果
101+
vector<vector<int>> result;
102+
vector<int> path;
41103
void backtracking(int n, int k, int startIndex) {
42104
if (path.size() == k) {
43105
result.push_back(path);
44106
return;
45107
}
46-
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) {
108+
for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方
47109
path.push_back(i); // 处理节点
48110
backtracking(n, k, i + 1);
49111
path.pop_back(); // 回溯,撤销处理的节点
@@ -57,3 +119,11 @@ public:
57119
}
58120
};
59121
```
122+
123+
# 总结
124+
125+
本篇我们准对求组合问题的回溯法代码做了剪枝优化,这个优化如果不画图的话,其实不好理解,也不好讲清楚。
126+
127+
所以我依然是把整个回溯过程抽象为一颗树形结构,然后可以直观的看出,剪枝究竟是剪的哪里。
128+
129+
**就酱,学到了就帮Carl转发一下吧,让更多的同学知道这里!**

problems/0316.去除重复字母.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# 思路
2+
3+
// 浓浓的单调栈气息
4+
5+
这道题目一点都不简单,输入单调栈里情况比较多的题目。
6+
7+
需要解决如下问题:
8+
// 这不是单纯遇到小的 栈里就弹出,判断是否在栈里,情况2
9+
// 如何记录它之前有没有出现过呢,也就是情况1
10+
11+
12+
情况1:
13+
输入:"bbcaac"
14+
输出:"ac"
15+
预期结果:"bac"
16+
17+
情况2
18+
输入:"abacb"
19+
输出:"acb"
20+
预期结果:"abc"
21+
22+
情况3:
23+
aba 输出 a 预期是ab
24+
25+
```
26+
class Solution {
27+
public:
28+
string removeDuplicateLetters(string s) {
29+
int letterCount[26] = {0};
30+
for (int i = 0; i < s.size(); i++) {
31+
letterCount[s[i] - 'a']++;
32+
}
33+
bool isIn[26] = {false}; // 1 已经在栈里,0 不在栈里
34+
string st;
35+
for (int i = 0; i < s.size(); i++) {
36+
while(!st.empty()
37+
&& s[i] < st.back()
38+
&& letterCount[st.back() - 'a'] > 0 // 保证字符串i之后还有这个栈顶元素,栈才能做弹出操作,情况3
39+
&& isIn[s[i] - 'a'] == false) { // 如果栈里已经有s[i]了,跳过:情况2
40+
isIn[st.back() - 'a'] = false;
41+
st.pop_back();
42+
}
43+
if (isIn[s[i] - 'a'] == false) {
44+
st.push_back(s[i]);
45+
isIn[s[i] - 'a'] = true;
46+
}
47+
letterCount[s[i] - 'a']--; // 只要用过了就减一:情况1
48+
}
49+
return st;
50+
}
51+
};
52+
```

problems/0332.重新安排行程.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11

22
## 题目地址
3+
https://leetcode-cn.com/problems/reconstruct-itinerary/
34

4-
5-
6-
# 第242题.有效的字母异位词
5+
# 332. 重新安排行程
76

87
# 思路
98

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
## 思路
3+
4+
这道题目数组在是哈希法中的经典应用,如果对数组在哈希法中的使用还不熟悉的同学可以看这两篇:[数组在哈希法中的应用](https://mp.weixin.qq.com/s/vM6OszkM6L1Mx2Ralm9Dig)[哈希法:383. 赎金信](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ)
5+
6+
进而可以学习一下[set在哈希法中的应用](https://mp.weixin.qq.com/s/N9iqAchXreSVW7zXUS4BVA),以及[map在哈希法中的应用](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ)
7+
8+
回归本题,**本题强调了-1000 <= arr[i] <= 1000**,那么就可以用数组来做哈希,arr[i]作为哈希表(数组)的下标,那么arr[i]可以是负数,怎么办?负数不能做数组下标。
9+
10+
**PS:本文[leetcode刷题攻略](https://github.com/youngyangyang04/leetcode-master)已经收录,相信可以帮你稳稳的提升算法能力,给star支持一下吧!**
11+
12+
13+
**此时可以定义一个2000大小的数组,例如int count[2002];**,统计的时候,将arr[i]统一加1000,这样就可以统计arr[i]的出现频率了。
14+
15+
题目中要求的是是否有相同的频率出现,那么需要再定义一个哈希表(数组)用来记录频率是否重复出现过,bool fre[1002]; 定义布尔类型的就可以了,**因为题目中强调1 <= arr.length <= 1000,所以哈希表大小为1000就可以了**
16+
17+
如图所示:
18+
19+
20+
<img src='../pics/1207.独一无二的出现次数.png' width=600> </img></div>
21+
22+
C++代码如下:
23+
24+
```
25+
class Solution {
26+
public:
27+
bool uniqueOccurrences(vector<int>& arr) {
28+
int count[2002] = {0}; // 统计数字出现的频率
29+
for (int i = 0; i < arr.size(); i++) {
30+
count[arr[i] + 1000]++;
31+
}
32+
bool fre[1002] = {false}; // 看相同频率是否重复出现
33+
for (int i = 0; i <= 2000; i++) {
34+
if (count[i]) {
35+
if (fre[count[i]] == false) fre[count[i]] = true;
36+
else return false;
37+
}
38+
}
39+
return true;
40+
}
41+
};
42+
```
43+
> **我是[程序员Carl](https://github.com/youngyangyang04),更多[精彩算法文章](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzUxNjY5NTYxNA==&action=getalbum&album_id=1485825793120387074&scene=173#wechat_redirect)尽在:[代码随想录](https://img-blog.csdnimg.cn/20200815195519696.png),你值得关注!**
44+
45+
**如果感觉题解对你有帮助,不要吝啬给一个👍吧!**

0 commit comments

Comments
 (0)