Skip to content

Commit f4c0e52

Browse files
Update
1 parent b4bca71 commit f4c0e52

10 files changed

+151
-58
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
* [这五道数组相关的面试题,你一定要会!](https://mp.weixin.qq.com/s/vdKHt2vFSZEouZASjdWieg)
1616
* [关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)
1717
* [这六道哈希表相关的面试题,你一定要会!](https://mp.weixin.qq.com/s/nxuWv5cUhCPSbAdIHtWgSg)
18+
* [关于链表,你该了解这些!](https://mp.weixin.qq.com/s/ntlZbEdKgnFQKZkSUAOSpQ)
19+
* [刷leetcode的时候,究竟什么时候可以使用库函数,什么时候不要使用库函数,过来人来说一说](https://leetcode-cn.com/circle/article/E1Kjzn/)
1820
* 精选链表相关的面试题
1921
* 精选字符串相关的面试题
2022
* 精选栈与队列相关的面试题

problems/0020.有效的括号.md

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,47 @@
11

22
## 题目地址
33

4-
https://leetcode-cn.com/problems/valid-parentheses/
4+
https://leetcode-cn.com/problems/valid-parentheses/
55

66
## 思路
77

8+
### 题外话
89
括号匹配是使用栈解决的经典问题。
910

1011
题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,响应的位置必须要有右括号,如果还记得编译原理的话,编译器在 词法分析的过程中处理 括号,花括号等这个符号的逻辑,也是使用了栈这种数据结构。
1112

1213
所以栈在计算机领域中应用是非常广泛的。 有的同学可以经常会想学的这些数据结构有什么用,也开发不了什么软件,大多数同学说的软件应该都是可视化的软件例如APP之类的,那都是非常上层的应用了,底层很多功能的实现都是基础的数据结构和算法。 这里我就不过多展开了,我们先来看题。
1314

15+
### 进入正题
16+
1417
由于栈结构的特殊性,非常适合做对称匹配类的题目。首先我们要弄清楚,字符串里的括号不匹配有几种情况。
1518

1619
一些同学,在面试中看到这种题目上来就开始写代码,建议要写代码之前要分析好有哪几种不匹配的情况,如果不动手之前分析好,写出的代码也会有很多问题。 会给面试官留下不好的印象。
1720

1821
我们先来分析一下 这里有三种不匹配的情况,
1922

20-
第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
21-
第二种情况,括号没有多余,但是 括号的类型没有匹配上。
22-
第三种情况,字符串里右方向的括号多余了,所以不匹配。
23+
1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
24+
![括号匹配1](https://img-blog.csdnimg.cn/2020080915505387.png)
25+
2. 第二种情况,括号没有多余,但是 括号的类型没有匹配上。
26+
![括号匹配2](https://img-blog.csdnimg.cn/20200809155107397.png)
27+
3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。
28+
![括号匹配3](https://img-blog.csdnimg.cn/20200809155115779.png)
2329

2430
我们的代码只要覆盖了这三种不匹配的情况,基本就不会出问题,可以看出 动手之前分析好题目的重要性。
2531

32+
动画如下:
33+
34+
<video src='../video/20.有效括号.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
35+
36+
37+
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false
38+
39+
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false
40+
41+
第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
42+
43+
那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈也是空的,就说明全都匹配了。
44+
2645
接下来我们来看一下代码。
2746

2847
## C++代码

problems/0094.二叉树的中序遍历.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ https://leetcode-cn.com/problems/binary-tree-inorder-traversal/
33

44
## 思路
55

6-
7-
8-
9-
094.二叉树的中序遍历
10-
144.二叉树的前序遍历
11-
145.二叉树的后序遍历
12-
建议一起做一下
6+
详细题解请看这篇:[一文学通二叉树前中后序递归法与迭代法](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)
137

148
## C++代码
159

problems/0102.二叉树的层序遍历.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,19 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
33

44
## 思路
55

6-
使用队列实现广度优先遍历
6+
我们之前讲过了,二叉树的深度优先遍历:[一文学通二叉树前中后序递归法与迭代法](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)里面有前中后序遍历的方式,前中后序分辨可以使用递归和迭代的方法来实现,接下来我们再来介绍二叉树的另一种遍历方式,也就是层序遍历。
7+
8+
层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。
9+
10+
需要借用一个辅助数据结构队列来实现,**队列先进后出,符合一层一层遍历的逻辑,而是用栈先进先出适合模拟深度优先遍历也就是递归的逻辑。**
11+
12+
使用队列实现广度优先遍历,动画如下:
13+
14+
<video src='../video/102二叉树的层序遍历.mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
15+
16+
这样就实现了层序从左到右遍历二叉树。
17+
18+
代码如下:这份代码也可以作为二叉树层序遍历的模板。
719

820
## C++代码
921

problems/0144.二叉树的前序遍历.md

Lines changed: 84 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,27 @@ https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
33

44
## 思路
55

6-
这篇文章,彻底讲清楚应该如何写递归,并给出了前中后序三种不同的迭代法,最后分析为什么迭代法代码风格不能统一,最后给出统一的前中后序迭代法的代码,帮大家彻底吃透二叉树的深度优先遍历。
6+
这篇文章,**彻底讲清楚应该如何写递归,并给出了前中后序三种不同的迭代法,然后分析迭代法的代码风格为什么没有统一,最后给出统一的前中后序迭代法的代码,帮大家彻底吃透二叉树的深度优先遍历。**
77

8-
这里想帮大家一下,明确一下二叉树的遍历规则
98

109
* 二叉树深度优先遍历
11-
* 前序遍历: 144.二叉树的前序遍历
12-
* 后序遍历: 145.二叉树的后序遍历
13-
* 中序遍历: 094.二叉树的中序遍历
10+
* 前序遍历: [0144.二叉树的前序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)
11+
* 后序遍历: [0145.二叉树的后序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0145.二叉树的后序遍历.md)
12+
* 中序遍历: [0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md)
1413
* 二叉树广度优先遍历
15-
* 层序遍历:0145.二叉树的后序遍历
14+
* 层序遍历:[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
1615

17-
这几道题目建议大家都做一下,本题解先只写二叉树深度优先遍历,二叉树广度优先遍历请看[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
16+
这几道题目建议大家都做一下,本题解先只写二叉树深度优先遍历,二叉树广度优先遍历请看题解[0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
17+
18+
这里想帮大家一下,明确一下二叉树的遍历规则:
1819

1920
![二叉树前后中遍历](https://img-blog.csdnimg.cn/20200808191505393.png)
2021

21-
以上述中,前中后序遍历顺序如下
22-
前序遍历(中左右):5 4 1 2 6 7 8
23-
中序遍历(左中右):1 4 2 5 7 6 8
24-
后序遍历(左右中):1 2 4 7 8 6 5
22+
以上述中,前中后序遍历顺序如下:
23+
24+
* 前序遍历(中左右):5 4 1 2 6 7 8
25+
* 中序遍历(左中右):1 4 2 5 7 6 8
26+
* 后序遍历(左右中):1 2 4 7 8 6 5
2527

2628
### 递归法
2729

@@ -31,15 +33,11 @@ https://leetcode-cn.com/problems/binary-tree-preorder-traversal/
3133

3234

3335
1. **确定递归函数的参数和返回值:**
34-
3536
确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
3637

37-
3838
2. **确定终止条件:**
39-
4039
写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
4140

42-
4341
3. **确定单层递归的逻辑:**
4442
确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
4543

@@ -67,11 +65,9 @@ traversal(cur->left, vec); // 左
6765
traversal(cur->right, vec); // 右
6866
```
6967

70-
单层递归的逻辑就是按照中左右的顺序来处理的
68+
单层递归的逻辑就是按照中左右的顺序来处理的,这样二叉树的前序遍历,基本就写完了,在看一下完整代码:
7169

72-
这样二叉树的前序遍历,基本就写完了,在看一下完整代码:
73-
74-
前序遍历
70+
前序遍历:
7571

7672
```
7773
class Solution {
@@ -90,7 +86,7 @@ public:
9086
};
9187
```
9288

93-
中序遍历
89+
中序遍历
9490

9591
```
9692
void traversal(TreeNode* cur, vector<int>& vec) {
@@ -101,7 +97,7 @@ public:
10197
}
10298
```
10399

104-
后序遍历
100+
后序遍历
105101

106102
```
107103
void traversal(TreeNode* cur, vector<int>& vec) {
@@ -119,6 +115,7 @@ public:
119115
那么接下来我先带大家看一看其中的根本原因,其实是可以针对三种遍历方式,使用迭代法可以写出统一风格的代码。
120116

121117
前序遍历(迭代法)不难写出如下代码:
118+
122119
```
123120
class Solution {
124121
public:
@@ -141,7 +138,7 @@ public:
141138

142139
这时会发现貌似使用迭代法写出先序遍历并不难,确实不难,但难却难在,我们再用迭代法写中序遍历的时候,发现套路又不一样了,目前的这个逻辑无法直接应用到中序遍历上。
143140

144-
#### 为什么迭代法不容易写出统一风格的代码
141+
#### 前后中遍历迭代法不统一的写法
145142

146143
为了解释清楚,我说明一下 刚刚在迭代的过程中,其实我们有两个操作,**一个是处理:将元素放进result数组中,一个是访问:遍历节点。**
147144

@@ -206,15 +203,15 @@ public:
206203
207204
```
208205

209-
此时我们实现了前后中遍历的三种迭代法,是不是发现迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。
206+
此时我们实现了前后中遍历的三种迭代法,**是不是发现迭代法实现的先中后序,其实风格也不是那么统一,除了先序和后序,有关联,中序完全就是另一个风格了,一会用栈遍历,一会又用指针来遍历。**
210207

211-
重头戏来了,接下来介绍一下统一写法。
208+
**重头戏来了,接下来介绍一下统一写法。**
212209

213-
#### 迭代法统一写法
210+
#### 前后中遍历迭代法统一的写法
214211

215212
我们以中序遍历为例,之前说使用栈的话,**无法同时解决处理过程和访问过程不一致的情况**,那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记,标记就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。
216213

217-
代码如下
214+
中序遍历代码如下
218215
```
219216
class Solution {
220217
public:
@@ -248,12 +245,66 @@ public:
248245

249246
<video src='../video/中序遍历迭代(统一写法).mp4' controls='controls' width='640' height='320' autoplay='autoplay'> Your browser does not support the video tag.</video></div>
250247

248+
前序遍历代码如下:
249+
250+
```
251+
class Solution {
252+
public:
253+
vector<int> preorderTraversal(TreeNode* root) {
254+
vector<int> result;
255+
stack<TreeNode*> st;
256+
if (root != NULL) st.push(root);
257+
while (!st.empty()) {
258+
TreeNode* node = st.top();
259+
if (node != NULL) {
260+
st.pop();
261+
if (node->right) st.push(node->right); // 右
262+
if (node->left) st.push(node->left); // 左
263+
st.push(node); // 中
264+
st.push(NULL);
265+
} else {
266+
st.pop();
267+
node = st.top();
268+
st.pop();
269+
result.push_back(node->val);
270+
}
271+
}
272+
return result;
273+
}
274+
};
275+
```
276+
277+
后续遍历代码如下:
251278

252-
大家在看一下具体代码实现
279+
```
280+
class Solution {
281+
public:
282+
vector<int> postorderTraversal(TreeNode* root) {
283+
vector<int> result;
284+
stack<TreeNode*> st;
285+
if (root != NULL) st.push(root);
286+
while (!st.empty()) {
287+
TreeNode* node = st.top();
288+
if (node != NULL) {
289+
st.pop();
290+
st.push(node); // 中
291+
st.push(NULL);
292+
293+
if (node->right) st.push(node->right); // 右
294+
if (node->left) st.push(node->left); // 左
253295
254-
接下来给大家介绍一种统一的写法。
296+
} else {
297+
st.pop();
298+
node = st.top();
299+
st.pop();
300+
result.push_back(node->val);
301+
}
302+
}
303+
return result;
304+
}
305+
};
306+
```
255307

256-
我们再来看一下代码。
257308
## C++代码
258309

259310
### 递归
@@ -296,7 +347,7 @@ public:
296347
```
297348

298349
### 栈 通用模板
299-
详细代码注释看 [0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md)
350+
300351
```
301352
class Solution {
302353
public:
@@ -308,9 +359,9 @@ public:
308359
TreeNode* node = st.top();
309360
if (node != NULL) {
310361
st.pop();
311-
if (node->right) st.push(node->right);
312-
if (node->left) st.push(node->left);
313-
st.push(node);
362+
if (node->right) st.push(node->right); // 右
363+
if (node->left) st.push(node->left); // 左
364+
st.push(node); // 中
314365
st.push(NULL);
315366
} else {
316367
st.pop();

problems/0145.二叉树的后序遍历.md

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@ https://leetcode-cn.com/problems/binary-tree-postorder-traversal/
33

44
## 思路
55

6-
094.二叉树的中序遍历
7-
144.二叉树的前序遍历
8-
145.二叉树的后序遍历
9-
10-
建议一起做一下
6+
详细题解请看这篇:[一文学通二叉树前中后序递归法与迭代法](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)
117

128
## C++代码
139

@@ -71,11 +67,11 @@ public:
7167
TreeNode* node = st.top();
7268
if (node != NULL) {
7369
st.pop();
74-
st.push(node); // 中
70+
st.push(node); // 中
7571
st.push(NULL);
7672
77-
if (node->right) st.push(node->right); // 右
78-
if (node->left) st.push(node->left); // 左
73+
if (node->right) st.push(node->right); // 右
74+
if (node->left) st.push(node->left); // 左
7975
8076
} else {
8177
st.pop();

problems/0349.两个数组的交集.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/
44

55
## 思路
66

7-
这道题目,主要要学会使用一种哈希数据结构,unordered_set,这个数据结构可以解决很多类似的问题
7+
这道题目,主要要学会使用一种哈希数据结构,unordered_set,这个数据结构可以解决很多类似的问题
88

9-
注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序
9+
注意题目特意说明:**输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序**
1010

11-
这道题用暴力的解法时间复杂度是O(n^2),这种解法面试官一定不会满意,那我们看看使用哈希法进一步优化
11+
这道题用暴力的解法时间复杂度是O(n^2),这种解法面试官一定不会满意,那我们看看使用哈希法进一步优化
1212

1313
那么可以发现,貌似用数组做哈希表可以解决这道题目,把nums1的元素,映射到哈希数组的下表上,然后在遍历nums2的时候,判断是否出现过就可以了。
1414

@@ -22,7 +22,10 @@ https://leetcode-cn.com/problems/intersection-of-two-arrays/
2222
* std::multiset
2323
* std::unordered_set
2424

25-
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,我们并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set
25+
std::set和std::multiset底层实现都是红黑树,std::unordered_set的底层实现是哈希表, 使用unordered_set 读写效率是最高的,我们并不需要对数据进行排序,而且还不要让数据重复,所以选择unordered_set。
26+
27+
思路如图所示:
28+
![set哈希法](https://img-blog.csdnimg.cn/2020080918570417.png)
2629

2730
## 代码
2831
```

problems/0707.设计链表.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,15 @@ https://leetcode-cn.com/problems/design-linked-list/
44

55
## 思路
66

7-
这道题目设计链表的五个接口
7+
如果链表的基础知识还不太懂,可以看这篇文章:[关于链表,你该了解这些!](https://mp.weixin.qq.com/s/ntlZbEdKgnFQKZkSUAOSpQ)
8+
9+
删除链表节点:
10+
![链表-删除节点](https://img-blog.csdnimg.cn/20200806195114541.png)
11+
12+
添加链表节点:
13+
![链表-添加节点](https://img-blog.csdnimg.cn/20200806195134331.png)
14+
15+
这道题目设计链表的五个接口:
816
* 获取链表第index个节点的数值
917
* 在链表的最前面插入一个节点
1018
* 在链表的最后面插入一个节点
@@ -13,6 +21,14 @@ https://leetcode-cn.com/problems/design-linked-list/
1321

1422
可以说这五个接口,已经覆盖了链表的常见操作,是练习链表操作非常好的一道题目
1523

24+
**链表操作的两种方式:**
25+
26+
1. 直接使用原来的链表来进行操作。
27+
2. 设置一个虚拟头结点在进行操作。
28+
29+
下面我采用的设置一个虚拟头结点(这样更方便一些,大家看代码就会感受出来)。
30+
31+
1632
## 代码
1733
```
1834
class MyLinkedList {
2.51 MB
Binary file not shown.

video/20.有效括号.mp4

1.35 MB
Binary file not shown.

0 commit comments

Comments
 (0)