diff --git a/README.md b/README.md
index 4609858a..a8427da5 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,8 @@
* [字符串:前缀表不右移,难道就写不出KMP了?](https://mp.weixin.qq.com/s/p3hXynQM2RRROK5c6X7xfw)
* [字符串:总结篇!](https://mp.weixin.qq.com/s/gtycjyDtblmytvBRFlCZJg)
+* [双指针法:总结篇!](https://mp.weixin.qq.com/s/_p7grwjISfMh0U65uOyCjA)
+
* 栈与队列
* [栈与队列:来看看栈和队列不为人知的一面](https://mp.weixin.qq.com/s/VZRjOccyE09aE-MgLbCMjQ)
* [栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/P6tupDwRFi6Ay-L7DT4NVg)
@@ -76,6 +78,9 @@
* [栈与队列:有没有想过计算机是如何处理表达式的?](https://mp.weixin.qq.com/s/hneh2nnLT91rR8ms2fm_kw)
* [栈与队列:滑动窗口里求最大值引出一个重要数据结构](https://mp.weixin.qq.com/s/8c6l2bO74xyMjph09gQtpA)
* [栈与队列:求前 K 个高频元素和队列有啥关系?](https://mp.weixin.qq.com/s/8hMwxoE_BQRbzCc7CA8rng)
+ * [栈与队列:总结篇!](https://mp.weixin.qq.com/s/xBcHyvHlWq4P13fzxEtkPg)
+* 二叉树
+ * [关于二叉树,你该了解这些!](https://mp.weixin.qq.com/s/_ymfWYvTNd2GvWvC5HOE4A)
(持续更新中....)
@@ -242,6 +247,7 @@
|[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**|
|[0486.预测赢家](https://github.com/youngyangyang04/leetcode/blob/master/problems/0486.预测赢家.md) |动态规划 |中等| **递归** **记忆递归** **动态规划**|
|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯算法**|
+|[0538.把二叉搜索树转换为累加树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0538.把二叉搜索树转换为累加树.md) |二叉树 |简单|**递归** **迭代**|
|[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**|
|[0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md) |哈希表 |简单|**哈希**|
|[0617.合并二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0617.合并二叉树.md) |树 |简单|**递归** **迭代**|
diff --git a/pics/538.把二叉搜索树转换为累加树.png b/pics/538.把二叉搜索树转换为累加树.png
new file mode 100644
index 00000000..1417fef9
Binary files /dev/null and b/pics/538.把二叉搜索树转换为累加树.png differ
diff --git a/pics/968.监控二叉树1.png b/pics/968.监控二叉树1.png
new file mode 100644
index 00000000..7e6a75df
Binary files /dev/null and b/pics/968.监控二叉树1.png differ
diff --git a/pics/968.监控二叉树2.png b/pics/968.监控二叉树2.png
new file mode 100644
index 00000000..664a656e
Binary files /dev/null and b/pics/968.监控二叉树2.png differ
diff --git a/pics/968.监控二叉树3.png b/pics/968.监控二叉树3.png
new file mode 100644
index 00000000..0a3a9ea6
Binary files /dev/null and b/pics/968.监控二叉树3.png differ
diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md
index b5998a45..04520204 100644
--- a/problems/0102.二叉树的层序遍历.md
+++ b/problems/0102.二叉树的层序遍历.md
@@ -7,6 +7,7 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/
相似题目:
* [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md)
+* [0107.二叉树的层次遍历II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0107.二叉树的层次遍历II.md)
* [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md)
* [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md)
diff --git a/problems/0538.把二叉搜索树转换为累加树.md b/problems/0538.把二叉搜索树转换为累加树.md
new file mode 100644
index 00000000..f50a03f1
--- /dev/null
+++ b/problems/0538.把二叉搜索树转换为累加树.md
@@ -0,0 +1,116 @@
+
+## 题目地址
+
+https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
+
+## 思路
+
+一看到累加树,相信很多小伙伴一脸懵逼,如何累加,遇到一个节点,然后在遍历其他节点累加?怎么一想这么麻烦呢。
+
+然后发现这是一颗二叉搜索树,二叉搜索树啊,这是有序的啊。
+
+那么有序的元素如果求累加呢?
+
+**其实这就是一棵树,大家可能看起来有点别扭,换一个角度来看,这就是一个有序数组[2, 5, 13],求从后到前的累加数组,也就是[20, 18, 13],大家是不是感觉这就是送分题了。**
+
+为什么变成数组就是送分题了呢,因为数组大家都知道怎么遍历啊,从后向前,挨个累加就完事了,这换成了二叉搜索树,看起来就别扭了一些是不是。
+
+那么知道如何遍历这个二叉树,也就迎刃而解了,从树中可以看出累加的顺讯是 右中左,所以我们需要中序遍历反过来遍历这个二叉树,然后顺序累加就可以了。
+
+遍历顺序如图所示:
+
+
+
+
+以下我给出一种递归的写法,两种迭代法的写法,别问我为什么写出了这么多写法,把我写的这个题解[彻底吃透二叉树的前中后序递归法和迭代法!!](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/che-di-chi-tou-er-cha-shu-de-qian-zhong-hou-xu-d-2/)看了,你也能分分钟写出来三种写法![机智]
+
+## C++递归代码
+
+```
+class Solution {
+private:
+ int pre; // 记录前一个节点的数值
+ void traversal(TreeNode* cur) { // 右中左遍历
+ if (cur == NULL) return;
+ traversal(cur->right);
+ cur->val += pre;
+ pre = cur->val;
+ traversal(cur->left);
+ }
+public:
+ TreeNode* convertBST(TreeNode* root) {
+ pre = 0;
+ traversal(root);
+ return root;
+ }
+};
+```
+
+## C++迭代法(一)代码
+
+```
+class Solution {
+private:
+ int pre; // 记录前一个节点的数值
+ void traversal(TreeNode* root) {
+ stack st;
+ TreeNode* cur = root;
+ while (cur != NULL || !st.empty()) {
+ if (cur != NULL) {
+ st.push(cur);
+ cur = cur->right; // 右
+ } else {
+ cur = st.top(); // 中
+ st.pop();
+ cur->val += pre;
+ pre = cur->val;
+ cur = cur->left; // 左
+ }
+ }
+ }
+public:
+ TreeNode* convertBST(TreeNode* root) {
+ pre = 0;
+ traversal(root);
+ return root;
+ }
+};
+```
+
+## C++迭代法(二)代码
+
+```
+class Solution {
+private:
+ int pre; // 记录前一个节点的数值
+ void traversal(TreeNode* root) {
+ stack st;
+ if (root != NULL) st.push(root);
+ while (!st.empty()) {
+ TreeNode* node = st.top();
+ if (node != NULL) {
+ st.pop();
+ if (node->left) st.push(node->left); // 左
+
+ st.push(node); // 中
+ st.push(NULL);
+
+ if (node->right) st.push(node->right); // 右
+ } else {
+ st.pop();
+ node = st.top();
+ st.pop();
+ node->val += pre; // 处理中间节点
+ pre = node->val;
+ }
+ }
+ }
+
+public:
+ TreeNode* convertBST(TreeNode* root) {
+ pre = 0;
+ traversal(root);
+ return root;
+ }
+};
+```
diff --git a/problems/0968.监控二叉树.md b/problems/0968.监控二叉树.md
new file mode 100644
index 00000000..613a5a5c
--- /dev/null
+++ b/problems/0968.监控二叉树.md
@@ -0,0 +1,217 @@
+# 题目地址
+https://leetcode-cn.com/problems/binary-tree-cameras/
+
+## 思路
+
+这道题目其实不是那么好理解的,题目举的示例不是很典型,会误以为摄像头必须要放在中间,其实放哪里都可以只要覆盖了就行。
+
+这道题目难在两点:
+
+1. 需要确定遍历方式
+2. 需要状态转移的方程
+
+我们之前做动态规划的时候,只要最难的地方在于确定状态转移方程,至于遍历方式无非就是在数组或者二维数组上。
+
+**而本题,不仅要确定状态转移方式,而且要在树上进行推导,所以难度就上来了,一些同学知道这道题目难,但其实说不上难点究竟在哪。**
+
+1. 需要确定遍历方式
+
+首先先确定遍历方式,才能确定转移方程,那么该如何遍历呢?
+
+在安排选择摄像头的位置的时候,**我们要从底向上进行推导,因为尽量让叶子节点的父节点安装摄像头,这样摄像头的数量才是最少的**
+
+如何从低向上推导呢?
+
+就是后序遍历也就是左右中的顺序,这样就可以从下到上进行推导了。
+
+后序遍历代码如下:
+
+```
+ int traversal(TreeNode* cur) {
+
+ // 空节点,该节点有覆盖
+ if (终止条件) return ;
+
+ int left = traversal(cur->left); // 左
+ int right = traversal(cur->right); // 右
+
+ 逻辑处理 // 中
+
+ return ;
+ }
+```
+
+**注意在以上代码中我们取了左孩子的返回值,右孩子的返回值,即left 和 right, 以后推导中间节点的状态**
+
+2. 需要状态转移的方程
+
+确定了遍历顺序,再看看这个状态应该如何转移,先来看看每个节点可能有几种状态:
+
+可以说有如下三种:
+
+* 该节点无覆盖
+* 本节点有摄像头
+* 本节点有覆盖
+
+我们分别有三个数字来表示:
+
+* 0:该节点无覆盖
+* 1:本节点有摄像头
+* 2:本节点有覆盖
+
+大家应该找不出第四个节点的状态了。
+
+那么问题来了,空节点究竟是哪一种状态呢? 空节点表示无覆盖? 表示有摄像头?还是有覆盖呢?
+
+回归本质,为了让摄像头数量最少,我们要尽量让叶子节点的父节点安装摄像头,这样才能摄像头的数量最少。
+
+那么空节点不能是无覆盖的状态,这样叶子节点就可以放摄像头了,空节点也不能是有摄像头的状态,这样叶子节点的父节点就没有必要放摄像头了,而是可以把摄像头放在叶子节点的爷爷节点上。
+
+**所以空节点的状态只能是有覆盖,这样就可以在叶子节点的父节点放摄像头了**
+
+接下来就是递推关系。
+
+那么递归的终止条件应该是遇到了空节点,此时应该返回2(有覆盖),原因上面已经解释过了。
+
+代码如下:
+
+```
+ // 空节点,该节点有覆盖
+ if (cur == NULL) return 2;
+```
+
+递归的函数,以及终止条件已经确定了,再来看单层逻辑处理。
+
+主要有如下四类情况:
+
+1. 情况1:左右节点都有覆盖
+
+左孩子有覆盖,右孩子有覆盖,那么此时中间节点应该就是无覆盖的状态了。
+
+如图:
+
+
+
+代码如下:
+
+```
+ // 左右节点都有覆盖
+ if (left == 2 && right == 2) return 0;
+```
+
+2. 情况2:左右节点至少有一个无覆盖的情况
+
+如果是以下情况,则中间节点(父节点)应该放摄像头:
+
+left == 0 && right == 0 左右节点无覆盖
+left == 1 && right == 0 左节点有摄像头,右节点无覆盖
+left == 0 && right == 1 左节点有无覆盖,右节点摄像头
+left == 0 && right == 2 左节点无覆盖,右节点覆盖
+left == 2 && right == 0 左节点覆盖,右节点无覆盖
+
+这个不难理解,毕竟有一个孩子没有覆盖,父节点就应该放摄像头。
+
+此时摄像头的数量要加一,并且return 1,代表中间节点放摄像头。
+
+代码如下:
+```
+ if (left == 0 || right == 0) {
+ result++;
+ return 1;
+ }
+```
+
+3. 情况3:左右节点至少有一个有摄像头
+
+如果是以下情况,其实就是 左右孩子节点有一个有摄像头了,那么其父节点就应该是2(覆盖的状态)
+
+left == 1 && right == 2 左节点有摄像头,右节点有覆盖
+left == 2 && right == 1 左节点有覆盖,右节点有摄像头
+left == 1 && right == 1 左右节点都有摄像头
+
+代码如下:
+
+```
+ if (left == 1 || right == 1) return 2;
+```
+
+**从这个代码中,可以看出,如果left == 1, right == 0 怎么办?其实这种条件在情况2中已经判断过了**,如图:
+
+
+
+这种情况也是大多数同学容易迷惑的情况。
+
+4. 情况4
+
+以上都处理完了,递归结束之后,可能头结点 还有一个无覆盖的情况,如图:
+
+
+
+所以递归结束之后,还要判断根节点,如果没有覆盖,result++,代码如下:
+
+```
+ int minCameraCover(TreeNode* root) {
+ result = 0;
+ if (traversal(root) == 0) { // root 无覆盖
+ result++;
+ }
+ return result;
+ }
+```
+
+以上四种情况我们分析完了,代码也差不多了,整体代码如下:
+
+(**以下我的代码是可以精简的,但是我是为了把情况说清楚,特别把每种情况列出来,因为精简之后的代码读者不好理解。**)
+
+## C++代码
+
+```
+class Solution {
+private:
+ int result;
+ int traversal(TreeNode* cur) {
+
+ // 空节点,该节点有覆盖
+ if (cur == NULL) return 2;
+
+ int left = traversal(cur->left); // 左
+ int right = traversal(cur->right); // 右
+
+ // 情况1
+ // 左右节点都有覆盖
+ if (left == 2 && right == 2) return 0;
+
+ // 情况2
+ // left == 0 && right == 0 左右节点无覆盖
+ // left == 1 && right == 0 左节点有摄像头,右节点无覆盖
+ // left == 0 && right == 1 左节点有无覆盖,右节点摄像头
+ // left == 0 && right == 2 左节点无覆盖,右节点覆盖
+ // left == 2 && right == 0 左节点覆盖,右节点无覆盖
+ if (left == 0 || right == 0) {
+ result++;
+ return 1;
+ }
+
+ // 情况3
+ // left == 1 && right == 2 左节点有摄像头,右节点有覆盖
+ // left == 2 && right == 1 左节点有覆盖,右节点有摄像头
+ // left == 1 && right == 1 左右节点都有摄像头
+ // 其他情况前段代码均已覆盖
+ if (left == 1 || right == 1) return 2;
+
+ // 以上代码我没有使用else,主要是为了把各个分支条件展现出来,这样代码有助于读者理解
+ // 这个 return -1 逻辑不会走到这里。
+ return -1;
+ }
+
+public:
+ int minCameraCover(TreeNode* root) {
+ result = 0;
+ // 情况4
+ if (traversal(root) == 0) { // root 无覆盖
+ result++;
+ }
+ return result;
+ }
+};
+```