diff --git a/README.md b/README.md
index 89bceef7..d568aeed 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,7 @@
* [二叉树:以为使用了递归,其实还隐藏着回溯](https://mp.weixin.qq.com/s/ivLkHzWdhjQQD1rQWe6zWA)
* [二叉树:做了这么多题目了,我的左叶子之和是多少?](https://mp.weixin.qq.com/s/gBAgmmFielojU5Wx3wqFTA)
* [二叉树:我的左下角的值是多少?](https://mp.weixin.qq.com/s/MH2gbLvzQ91jHPKqiub0Nw)
+ * [二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)
@@ -264,6 +265,7 @@
|[0116.填充每个节点的下一个右侧节点指针](https://github.com/youngyangyang04/leetcode/blob/master/problems/0116.填充每个节点的下一个右侧节点指针.md) |二叉树 |中等|**广度优先搜索**|
|[0117.填充每个节点的下一个右侧节点指针II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0117.填充每个节点的下一个右侧节点指针II.md) |二叉树 |中等|**广度优先搜索**|
|[0131.分割回文串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0131.分割回文串.md) |回溯 |中等|**回溯**|
+|[0141.环形链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0141.环形链表.md) |链表 |简单|**快慢指针/双指针**|
|[0142.环形链表II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0142.环形链表II.md) |链表 |中等|**快慢指针/双指针**|
|[0144.二叉树的前序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md) |树 |中等|**递归** **迭代/栈**|
|[0145.二叉树的后序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0145.二叉树的后序遍历.md) |树 |困难|**递归** **迭代/栈**|
diff --git a/pics/42.接雨水1.png b/pics/42.接雨水1.png
new file mode 100644
index 00000000..26624f4b
Binary files /dev/null and b/pics/42.接雨水1.png differ
diff --git a/pics/42.接雨水2.png b/pics/42.接雨水2.png
new file mode 100644
index 00000000..94eda97e
Binary files /dev/null and b/pics/42.接雨水2.png differ
diff --git a/pics/42.接雨水3.png b/pics/42.接雨水3.png
new file mode 100644
index 00000000..1e4b528a
Binary files /dev/null and b/pics/42.接雨水3.png differ
diff --git a/pics/654.最大二叉树.png b/pics/654.最大二叉树.png
new file mode 100644
index 00000000..e8fc63aa
Binary files /dev/null and b/pics/654.最大二叉树.png differ
diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md
index d1358c70..e5e3dac9 100644
--- a/problems/0017.电话号码的字母组合.md
+++ b/problems/0017.电话号码的字母组合.md
@@ -97,4 +97,8 @@ public:
};
```
+# 拓展
+
+请问为什么 getCombinations(const string& digits, int index, const string& s)函数里的string& s 前要加const,不加的报错
+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
diff --git a/problems/0042.接雨水.md b/problems/0042.接雨水.md
index 349d4102..4a166ae3 100644
--- a/problems/0042.接雨水.md
+++ b/problems/0042.接雨水.md
@@ -6,41 +6,103 @@
// 找左面最大的, 找右边最大的,找左右边际的时候容易迷糊。我已开始还找左大于右的。 (还不够)
// 每次记录单条,不要记录整个面积
-# C++代码
+## 暴力解法
+
+这道题目暴力解法并不简单,我们来看一下思路。
+
+首先要明确,要按照行来计算,还是按照列来计算。如图所示:
+
+
+
+
+
+一些同学在实现暴力解法的时候,很容易一会按照行来计算一会按照列来计算,这样就会越写越乱。
+
+我个人倾向于按照列来计算,比较容易理解,接下来看一下按照列如何计算。
+
+首先,**如果按照列来计算的话,宽度一定是1了,我们再把每一列的雨水的高度求出来就可以了。**
+
+可以看出每一列雨水的高度,取决于,该列 左侧最高的柱子和右侧最高的柱子中最矮的那个柱子的高度。
+
+这句话可以有点绕,来举一个理解,例如求列4的雨水高度,如图:
+
+
+
+列4 左侧最高的柱子是列3,高度为2(以下用lHeight表示)。
+
+列4 右侧最高的柱子是列7,高度为3(以下用rHeight表示)。
+
+列4 柱子的高度为1(以下用height表示)
+
+那么列4的雨水高度为 列3和列7的高度最小值减列4高度,即: min(lHeight, rHeight) - height。
+
+列4的雨水高度求出来了,宽度为1,相乘就是列4的雨水体积了。
+
+此时求出了列4的雨水体积。
+
+一样的方法,只要从头遍历一遍所有的列,然后求出每一列雨水的体积,相加之后就是总雨水的体积了。
+
+首先从头遍历所有的列,并且**要注意第一个柱子和最后一个柱子不接雨水**,代码如下:
+```
+for (int i = 0; i < height.size(); i++) {
+ // 第一个柱子和最后一个柱子不接雨水
+ if (i == 0 || i == height.size() - 1) continue;
+}
+```
+
+在for循环中求左右两边最高柱子,代码如下:
+
+```
+int rHeight = height[i]; // 记录右边柱子的最高高度
+int lHeight = height[i]; // 记录左边柱子的最高高度
+for (int r = i + 1; r < height.size(); r++) {
+ if (height[r] > rHeight) rHeight = height[r];
+}
+for (int l = i - 1; l >= 0; l--) {
+ if (height[l] > lHeight) lHeight = height[l];
+}
+```
+
+最后,计算该列的雨水高度,代码如下:
+
+```
+int h = min(lHeight, rHeight) - height[i];
+if (h > 0) sum += h; // 注意只有h大于零的时候,在统计到总和中
+```
+
+整体代码如下:
-按照列来
```
class Solution {
public:
int trap(vector& height) {
int sum = 0;
-// for (int i = 0; i < height.size(); i++) {
-// cout << height[i] << " ";
-// }
-// cout << endl;
for (int i = 0; i < height.size(); i++) {
+ // 第一个柱子和最后一个柱子不接雨水
if (i == 0 || i == height.size() - 1) continue;
- int lIndex, rIndex;
- int rValue = height[i];
- int lValue = height[i];
+ int rHeight = height[i]; // 记录右边柱子的最高高度
+ int lHeight = height[i]; // 记录左边柱子的最高高度
for (int r = i + 1; r < height.size(); r++) {
- if (height[r] > rValue) {
- rValue = height[r];
- rIndex = r;
- }
+ if (height[r] > rHeight) rHeight = height[r];
}
for (int l = i - 1; l >= 0; l--) {
- if (height[l] > lValue) {
- lValue = height[l];
- lIndex = l;
- }
+ if (height[l] > lHeight) lHeight = height[l];
}
- int h = min(lValue, rValue) - height[i];
- // 我为啥要算 (rIndex - lIndex + 1);就是按照行 按照列 区分不清啊
+ int h = min(lHeight, rHeight) - height[i];
if (h > 0) sum += h;
}
return sum;
}
};
```
+
+因为每次遍历列的时候,还要向两边寻找最高的列,所以时间复杂度为O(n^2)。
+空间复杂度为O(1)。
+
+
+# 单调栈
+
+单调栈究竟如何做呢,得画一个图,不太好理解
+
+按照列来算的,遇到相同的怎么办。
diff --git a/problems/0141.环形链表.md b/problems/0141.环形链表.md
index 4198ba4a..bdb12c5b 100644
--- a/problems/0141.环形链表.md
+++ b/problems/0141.环形链表.md
@@ -19,6 +19,11 @@ fast和slow各自再走一步, fast和slow就相遇了
这是因为fast是走两步,slow是走一步,**其实相对于slow来说,fast是一个节点一个节点的靠近slow的**,所以fast一定可以和slow重合。
+动画如下:
+
+
+
+
## C++代码如下
diff --git a/problems/0142.环形链表II.md b/problems/0142.环形链表II.md
index 35b323d4..27c61bf9 100644
--- a/problems/0142.环形链表II.md
+++ b/problems/0142.环形链表II.md
@@ -43,6 +43,11 @@ fast和slow各自再走一步, fast和slow就相遇了
这是因为fast是走两步,slow是走一步,**其实相对于slow来说,fast是一个节点一个节点的靠近slow的**,所以fast一定可以和slow重合。
+动画如下:
+
+
+
+
## 如果有环,如何找到这个环的入口
@@ -83,6 +88,11 @@ fast指针走过的节点数:` x + y + n (y + z)`,n为fast指针在环内走
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
+动画如下:
+
+
+
+
那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。
其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。
diff --git a/problems/0654.最大二叉树.md b/problems/0654.最大二叉树.md
index 0ea30f35..64efda5c 100644
--- a/problems/0654.最大二叉树.md
+++ b/problems/0654.最大二叉树.md
@@ -1,19 +1,35 @@
## 题目地址
https://leetcode-cn.com/problems/maximum-binary-tree/
+> 用数组构建二叉树都是一样的套路
+
+# 654.最大二叉树
+
+给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下:
+
+* 二叉树的根是数组中的最大元素。
+* 左子树是通过数组中最大值左边部分构造出的最大二叉树。
+* 右子树是通过数组中最大值右边部分构造出的最大二叉树。
+
+通过给定的数组构建最大二叉树,并且输出这个树的根节点。
+
+示例 :
+
+
+
+提示:
+
+给定的数组的大小在 [1, 1000] 之间。
+
## 思路
最大二叉树的构建过程如下:
-
+
-典型的递归问题,依然按照递归三部曲来分析:
+构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
-* 确定递归函数的参数和返回值
-* 确定终止条件
-* 确定单层递归的逻辑
-
-### 确定递归函数的参数和返回值
+* 确定递归函数的参数和返回值
参数就是传入的是存放元素的数组,返回该数组构造的二叉树的头结点,返回类型是指向节点的指针。
@@ -23,9 +39,11 @@ https://leetcode-cn.com/problems/maximum-binary-tree/
TreeNode* constructMaximumBinaryTree(vector& nums)
```
-### 确定终止条件
+* 确定终止条件
-题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了具体节点了,那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。
+题目中说了输入的数组大小一定是大于等于1的,所以我们不用考虑小于1的情况,那么当递归遍历的时候,如果传入的数组大小为1,说明遍历到了叶子节点了。
+
+那么应该定义一个新的节点,并把这个数组的数值赋给新的节点,然后返回这个节点。 这表示一个数组大小是1的时候,构造了一个新的节点,并返回。
代码如下:
@@ -36,11 +54,12 @@ if (nums.size() == 1) {
return node;
}
```
-### 确定单层递归的逻辑
+
+* 确定单层递归的逻辑
这里有三步工作
-1. 先要找到数组中最大的值和对应的下表, 最大的值就是根节点
+1. 先要找到数组中最大的值和对应的下表, 最大的值构造根节点,下表用来下一步分割数组。
代码如下:
```
@@ -82,8 +101,6 @@ if (maxValueIndex < (nums.size() - 1)) {
```
这样我们就分析完了,整体代码如下:(详细注释)
-## C++代码
-
```
class Solution {
public:
@@ -105,7 +122,7 @@ public:
node->val = maxValue;
// 最大值所在的下表左区间 构造左子树
if (maxValueIndex > 0) {
- vector newVec(nums.begin(), nums.begin() + maxValueIndex);
+ vector newVec(nums.begin(), nums.begin() + maxValueIndex);
node->left = constructMaximumBinaryTree(newVec);
}
// 最大值所在的下表右区间 构造右子树
@@ -117,4 +134,86 @@ public:
}
};
```
+
+以上代码比较冗余,效率也不高,每次还要切割的时候每次都要定义新的vector(也就是数组),但逻辑比较清晰。
+
+和文章[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg)中一样的优化思路,就是每次分隔不用定义新的数组,而是通过下表索引直接在原数组上操作。
+
+优化后代码如下:
+
+```
+class Solution {
+private:
+ // 在左闭右开区间[left, right),构造二叉树
+ TreeNode* traversal(vector& nums, int left, int right) {
+ if (left >= right) return nullptr;
+
+ // 分割点下表:maxValueIndex
+ int maxValueIndex = left;
+ for (int i = left + 1; i < right; ++i) {
+ if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;
+ }
+
+ TreeNode* root = new TreeNode(nums[maxValueIndex]);
+
+ // 左闭右开:[left, maxValueIndex)
+ root->left = traversal(nums, left, maxValueIndex);
+
+ // 左闭右开:[maxValueIndex + 1, right)
+ root->right = traversal(nums, maxValueIndex + 1, right);
+
+ return root;
+ }
+public:
+ TreeNode* constructMaximumBinaryTree(vector& nums) {
+ return traversal(nums, 0, nums.size());
+ }
+};
+```
+
+# 拓展
+
+可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空**
+
+第一版递归过程:(加了if判断,为了不让空节点进入递归)
+```
+
+if (maxValueIndex > 0) { // 这里加了判断是为了不让空节点进入递归
+ vector newVec(nums.begin(), nums.begin() + maxValueIndex);
+ node->left = constructMaximumBinaryTree(newVec);
+}
+
+if (maxValueIndex < (nums.size() - 1)) { // 这里加了判断是为了不让空节点进入递归
+ vector newVec(nums.begin() + maxValueIndex + 1, nums.end());
+ node->right = constructMaximumBinaryTree(newVec);
+}
+```
+
+第二版递归过程: (如下代码就没有加if判断)
+
+```
+root->left = traversal(nums, left, maxValueIndex);
+
+root->right = traversal(nums, maxValueIndex + 1, right);
+```
+
+第二版代码是允许空节点进入递归,所以没有加if判断,当然终止条件也要有相应的改变。
+
+第一版终止条件,是遇到叶子节点就终止,因为空节点不会进入递归。
+
+第二版相应的终止条件,是遇到空节点,也就是数组区间为0,就终止了。
+
+
+# 总结
+
+这道题目其实和 [二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 是一个思路,比[二叉树:构造二叉树登场!](https://mp.weixin.qq.com/s/7r66ap2s-shvVvlZxo59xg) 还简单一些。
+
+**注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下表索引直接在原数组上操作,这样可以节约时间和空间上的开销。**
+
+一些同学也会疑惑,什么时候递归函数前面加if,什么时候不加if,这个问题我在最后也给出了解释。
+
+其实就是不同代码风格的实现,**一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。**
+
+
+
> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。
diff --git a/video/141.环形链表.gif b/video/141.环形链表.gif
new file mode 100644
index 00000000..5e5308aa
Binary files /dev/null and b/video/141.环形链表.gif differ
diff --git a/video/141.环形链表.mp4 b/video/141.环形链表.mp4
new file mode 100644
index 00000000..19a7f223
Binary files /dev/null and b/video/141.环形链表.mp4 differ
diff --git a/video/142.环形链表II.mp4 b/video/142.环形链表II.mp4
new file mode 100644
index 00000000..b6ced104
Binary files /dev/null and b/video/142.环形链表II.mp4 differ
diff --git a/video/142.环形链表II1.mp4 b/video/142.环形链表II1.mp4
new file mode 100644
index 00000000..317ce246
Binary files /dev/null and b/video/142.环形链表II1.mp4 differ
diff --git a/video/654.最大二叉树.gif b/video/654.最大二叉树.gif
new file mode 100644
index 00000000..3baa8158
Binary files /dev/null and b/video/654.最大二叉树.gif differ