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

Commit 46c6c7c

Browse files
Update
1 parent e0bead0 commit 46c6c7c

8 files changed

+340
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@
6767
* [字符串:前缀表不右移,难道就写不出KMP了?](https://mp.weixin.qq.com/s/p3hXynQM2RRROK5c6X7xfw)
6868
* [字符串:总结篇!](https://mp.weixin.qq.com/s/gtycjyDtblmytvBRFlCZJg)
6969

70+
* [双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
71+
7072
* 栈与队列
7173
* [栈与队列:来看看栈和队列不为人知的一面](https://mp.weixin.qq.com/s/VZRjOccyE09aE-MgLbCMjQ)
7274
* [栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/P6tupDwRFi6Ay-L7DT4NVg)
@@ -76,6 +78,9 @@
7678
* [栈与队列:有没有想过计算机是如何处理表达式的?](https://mp.weixin.qq.com/s/hneh2nnLT91rR8ms2fm_kw)
7779
* [栈与队列:滑动窗口里求最大值引出一个重要数据结构](https://mp.weixin.qq.com/s/8c6l2bO74xyMjph09gQtpA)
7880
* [栈与队列:求前 K 个高频元素和队列有啥关系?](https://mp.weixin.qq.com/s/8hMwxoE_BQRbzCc7CA8rng)
81+
* [栈与队列:总结篇!](https://mp.weixin.qq.com/s/xBcHyvHlWq4P13fzxEtkPg)
82+
* 二叉树
83+
* [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A)
7984

8085
(持续更新中....)
8186

@@ -242,6 +247,7 @@
242247
|[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**|
243248
|[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**|
244249
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法**|
250+
|[0538.把二叉搜索树转换为累加树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0538.把二叉搜索树转换为累加树.md) |二叉树 |简单|**递归** **迭代**|
245251
|[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**|
246252
|[0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md) |哈希表 |简单|**哈希**|
247253
|[0617.合并二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0617.合并二叉树.md) ||简单|**递归** **迭代**|
Loading

pics/968.监控二叉树1.png

65.4 KB
Loading

pics/968.监控二叉树2.png

116 KB
Loading

pics/968.监控二叉树3.png

87.9 KB
Loading

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
77

88
相似题目:
99
* [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
10+
* [0107.二叉树的层次遍历II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0107.二叉树的层次遍历II.md)
1011
* [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md)
1112
* [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md)
1213

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
2+
## 题目地址
3+
4+
https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
5+
6+
## 思路
7+
8+
一看到累加树,相信很多小伙伴一脸懵逼,如何累加,遇到一个节点,然后在遍历其他节点累加?怎么一想这么麻烦呢。
9+
10+
然后发现这是一颗二叉搜索树,二叉搜索树啊,这是有序的啊。
11+
12+
那么有序的元素如果求累加呢?
13+
14+
**其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],大家是不是感觉这就是送分题了。**
15+
16+
为什么变成数组就是送分题了呢,因为数组大家都知道怎么遍历啊,从后向前,挨个累加就完事了,这换成了二叉搜索树,看起来就别扭了一些是不是。
17+
18+
那么知道如何遍历这个二叉树,也就迎刃而解了,从树中可以看出累加的顺讯是 右中左,所以我们需要中序遍历反过来遍历这个二叉树,然后顺序累加就可以了。
19+
20+
遍历顺序如图所示:
21+
22+
<img src='../pics/538.把二叉搜索树转换为累加树.png' width=600> </img></div>
23+
24+
25+
以下我给出一种递归的写法,两种迭代法的写法,别问我为什么写出了这么多写法,把我写的这个题解[彻底吃透二叉树的前中后序递归法和迭代法!!](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/che-di-chi-tou-er-cha-shu-de-qian-zhong-hou-xu-d-2/)看了,你也能分分钟写出来三种写法![机智]
26+
27+
## C++递归代码
28+
29+
```
30+
class Solution {
31+
private:
32+
int pre; // 记录前一个节点的数值
33+
void traversal(TreeNode* cur) { // 右中左遍历
34+
if (cur == NULL) return;
35+
traversal(cur->right);
36+
cur->val += pre;
37+
pre = cur->val;
38+
traversal(cur->left);
39+
}
40+
public:
41+
TreeNode* convertBST(TreeNode* root) {
42+
pre = 0;
43+
traversal(root);
44+
return root;
45+
}
46+
};
47+
```
48+
49+
## C++迭代法(一)代码
50+
51+
```
52+
class Solution {
53+
private:
54+
int pre; // 记录前一个节点的数值
55+
void traversal(TreeNode* root) {
56+
stack<TreeNode*> st;
57+
TreeNode* cur = root;
58+
while (cur != NULL || !st.empty()) {
59+
if (cur != NULL) {
60+
st.push(cur);
61+
cur = cur->right; // 右
62+
} else {
63+
cur = st.top(); // 中
64+
st.pop();
65+
cur->val += pre;
66+
pre = cur->val;
67+
cur = cur->left; // 左
68+
}
69+
}
70+
}
71+
public:
72+
TreeNode* convertBST(TreeNode* root) {
73+
pre = 0;
74+
traversal(root);
75+
return root;
76+
}
77+
};
78+
```
79+
80+
## C++迭代法(二)代码
81+
82+
```
83+
class Solution {
84+
private:
85+
int pre; // 记录前一个节点的数值
86+
void traversal(TreeNode* root) {
87+
stack<TreeNode*> st;
88+
if (root != NULL) st.push(root);
89+
while (!st.empty()) {
90+
TreeNode* node = st.top();
91+
if (node != NULL) {
92+
st.pop();
93+
if (node->left) st.push(node->left); // 左
94+
95+
st.push(node); // 中
96+
st.push(NULL);
97+
98+
if (node->right) st.push(node->right); // 右
99+
} else {
100+
st.pop();
101+
node = st.top();
102+
st.pop();
103+
node->val += pre; // 处理中间节点
104+
pre = node->val;
105+
}
106+
}
107+
}
108+
109+
public:
110+
TreeNode* convertBST(TreeNode* root) {
111+
pre = 0;
112+
traversal(root);
113+
return root;
114+
}
115+
};
116+
```

problems/0968.监控二叉树.md

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
# 题目地址
2+
https://leetcode-cn.com/problems/binary-tree-cameras/
3+
4+
## 思路
5+
6+
这道题目其实不是那么好理解的,题目举的示例不是很典型,会误以为摄像头必须要放在中间,其实放哪里都可以只要覆盖了就行。
7+
8+
这道题目难在两点:
9+
10+
1. 需要确定遍历方式
11+
2. 需要状态转移的方程
12+
13+
我们之前做动态规划的时候,只要最难的地方在于确定状态转移方程,至于遍历方式无非就是在数组或者二维数组上。
14+
15+
**而本题,不仅要确定状态转移方式,而且要在树上进行推导,所以难度就上来了,一些同学知道这道题目难,但其实说不上难点究竟在哪。**
16+
17+
1. 需要确定遍历方式
18+
19+
首先先确定遍历方式,才能确定转移方程,那么该如何遍历呢?
20+
21+
在安排选择摄像头的位置的时候,**我们要从底向上进行推导,因为尽量让叶子节点的父节点安装摄像头,这样摄像头的数量才是最少的**
22+
23+
如何从低向上推导呢?
24+
25+
就是后序遍历也就是左右中的顺序,这样就可以从下到上进行推导了。
26+
27+
后序遍历代码如下:
28+
29+
```
30+
int traversal(TreeNode* cur) {
31+
32+
// 空节点,该节点有覆盖
33+
if (终止条件) return ;
34+
35+
int left = traversal(cur->left); // 左
36+
int right = traversal(cur->right); // 右
37+
38+
逻辑处理 // 中
39+
40+
return ;
41+
}
42+
```
43+
44+
**注意在以上代码中我们取了左孩子的返回值,右孩子的返回值,即left 和 right, 以后推导中间节点的状态**
45+
46+
2. 需要状态转移的方程
47+
48+
确定了遍历顺序,再看看这个状态应该如何转移,先来看看每个节点可能有几种状态:
49+
50+
可以说有如下三种:
51+
52+
* 该节点无覆盖
53+
* 本节点有摄像头
54+
* 本节点有覆盖
55+
56+
我们分别有三个数字来表示:
57+
58+
* 0:该节点无覆盖
59+
* 1:本节点有摄像头
60+
* 2:本节点有覆盖
61+
62+
大家应该找不出第四个节点的状态了。
63+
64+
那么问题来了,空节点究竟是哪一种状态呢? 空节点表示无覆盖? 表示有摄像头?还是有覆盖呢?
65+
66+
回归本质,为了让摄像头数量最少,我们要尽量让叶子节点的父节点安装摄像头,这样才能摄像头的数量最少。
67+
68+
那么空节点不能是无覆盖的状态,这样叶子节点就可以放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上。
69+
70+
**所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了**
71+
72+
接下来就是递推关系。
73+
74+
那么递归的终止条件应该是遇到了空节点,此时应该返回2(有覆盖),原因上面已经解释过了。
75+
76+
代码如下:
77+
78+
```
79+
// 空节点,该节点有覆盖
80+
if (cur == NULL) return 2;
81+
```
82+
83+
递归的函数,以及终止条件已经确定了,再来看单层逻辑处理。
84+
85+
主要有如下四类情况:
86+
87+
1. 情况1:左右节点都有覆盖
88+
89+
左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。
90+
91+
如图:
92+
93+
<img src='../pics/968.监控二叉树2.png' width=600> </img></div>
94+
95+
代码如下:
96+
97+
```
98+
// 左右节点都有覆盖
99+
if (left == 2 && right == 2) return 0;
100+
```
101+
102+
2. 情况2:左右节点至少有一个无覆盖的情况
103+
104+
如果是以下情况,则中间节点(父节点)应该放摄像头:
105+
106+
left == 0 && right == 0 左右节点无覆盖
107+
left == 1 && right == 0 左节点有摄像头,右节点无覆盖
108+
left == 0 && right == 1 左节点有无覆盖,右节点摄像头
109+
left == 0 && right == 2 左节点无覆盖,右节点覆盖
110+
left == 2 && right == 0 左节点覆盖,右节点无覆盖
111+
112+
这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。
113+
114+
此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。
115+
116+
代码如下:
117+
```
118+
if (left == 0 || right == 0) {
119+
result++;
120+
return 1;
121+
}
122+
```
123+
124+
3. 情况3:左右节点至少有一个有摄像头
125+
126+
如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)
127+
128+
left == 1 && right == 2 左节点有摄像头,右节点有覆盖
129+
left == 2 && right == 1 左节点有覆盖,右节点有摄像头
130+
left == 1 && right == 1 左右节点都有摄像头
131+
132+
代码如下:
133+
134+
```
135+
if (left == 1 || right == 1) return 2;
136+
```
137+
138+
**从这个代码中,可以看出,如果left == 1, right == 0 怎么办?其实这种条件在情况2中已经判断过了**,如图:
139+
140+
<img src='../pics/968.监控二叉树1.png' width=600> </img></div>
141+
142+
这种情况也是大多数同学容易迷惑的情况。
143+
144+
4. 情况4
145+
146+
以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:
147+
148+
<img src='../pics/968.监控二叉树3.png' width=600> </img></div>
149+
150+
所以递归结束之后,还要判断根节点,如果没有覆盖,result++,代码如下:
151+
152+
```
153+
int minCameraCover(TreeNode* root) {
154+
result = 0;
155+
if (traversal(root) == 0) { // root 无覆盖
156+
result++;
157+
}
158+
return result;
159+
}
160+
```
161+
162+
以上四种情况我们分析完了,代码也差不多了,整体代码如下:
163+
164+
**以下我的代码是可以精简的,但是我是为了把情况说清楚,特别把每种情况列出来,因为精简之后的代码读者不好理解。**
165+
166+
## C++代码
167+
168+
```
169+
class Solution {
170+
private:
171+
int result;
172+
int traversal(TreeNode* cur) {
173+
174+
// 空节点,该节点有覆盖
175+
if (cur == NULL) return 2;
176+
177+
int left = traversal(cur->left); // 左
178+
int right = traversal(cur->right); // 右
179+
180+
// 情况1
181+
// 左右节点都有覆盖
182+
if (left == 2 && right == 2) return 0;
183+
184+
// 情况2
185+
// left == 0 && right == 0 左右节点无覆盖
186+
// left == 1 && right == 0 左节点有摄像头,右节点无覆盖
187+
// left == 0 && right == 1 左节点有无覆盖,右节点摄像头
188+
// left == 0 && right == 2 左节点无覆盖,右节点覆盖
189+
// left == 2 && right == 0 左节点覆盖,右节点无覆盖
190+
if (left == 0 || right == 0) {
191+
result++;
192+
return 1;
193+
}
194+
195+
// 情况3
196+
// left == 1 && right == 2 左节点有摄像头,右节点有覆盖
197+
// left == 2 && right == 1 左节点有覆盖,右节点有摄像头
198+
// left == 1 && right == 1 左右节点都有摄像头
199+
// 其他情况前段代码均已覆盖
200+
if (left == 1 || right == 1) return 2;
201+
202+
// 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
203+
// 这个 return -1 逻辑不会走到这里。
204+
return -1;
205+
}
206+
207+
public:
208+
int minCameraCover(TreeNode* root) {
209+
result = 0;
210+
// 情况4
211+
if (traversal(root) == 0) { // root 无覆盖
212+
result++;
213+
}
214+
return result;
215+
}
216+
};
217+
```

0 commit comments

Comments
 (0)