Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

![106. 从中序与后序遍历序列构造二叉树1](https://img-blog.csdnimg.cn/20210203154316774.png)

## 视频讲解
# 视频讲解

**《代码随想录》算法视频公开课:[坑很多!来看看你掉过几次坑 | LeetCode:106.从中序与后序遍历序列构造二叉树](https://www.bilibili.com/video/BV1vW4y1i7dn),相信结合视频在看本篇题解,更有助于大家对本题的理解**。

Expand Down
2 changes: 1 addition & 1 deletion problems/0142.环形链表II.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fast和slow各自再走一步, fast和slow就相遇了
环形入口节点到 fast指针与slow指针相遇节点 节点数为y。
从相遇节点 再到环形入口节点节点数为 z。 如图所示:

![142环形链表2](https://img-blog.csdnimg.cn/20210318162938397.png)
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220925103433.png)

那么相遇时:
slow指针走过的节点数为: `x + y`,
Expand Down
2 changes: 1 addition & 1 deletion problems/0150.逆波兰表达式求值.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public:
st.pop();
if (tokens[i] == "+") st.push(num2 + num1);
if (tokens[i] == "-") st.push(num2 - num1);
if (tokens[i] == "*") st.push(num2 * num1);
if (tokens[i] == "*") st.push((long)num2 * (long)num1); //力扣改了后台测试数据
if (tokens[i] == "/") st.push(num2 / num1);
} else {
st.push(stoi(tokens[i]));
Expand Down
20 changes: 14 additions & 6 deletions problems/0235.二叉搜索树的最近公共祖先.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,24 +38,32 @@
# 思路



做过[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)题目的同学应该知道,利用回溯从底向上搜索,遇到一个节点的左子树里有p,右子树里有q,那么当前节点就是最近公共祖先。

那么本题是二叉搜索树,二叉搜索树是有序的,那得好好利用一下这个特点。

在有序树里,如果判断一个节点的左子树里有p,右子树里有q呢?

其实只要从上到下遍历的时候,cur节点是数值在[p, q]区间中则说明该节点cur就是最近公共祖先了。
因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。

理解这一点,本题就很好解了。
那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。 那问题来了,**一定是最近公共祖先吗**?

如图,我们从根节点搜索,第一次遇到 cur节点是数值在[p, q]区间中,即 节点5,此时可以说明 p 和 q 一定分别存在于 节点 5的左子树,和右子树中。

![235.二叉搜索树的最近公共祖先](https://code-thinking-1253855093.file.myqcloud.com/pics/20220926164214.png)

和[二叉树:公共祖先问题](https://programmercarl.com/0236.二叉树的最近公共祖先.html)不同,普通二叉树求最近公共祖先需要使用回溯,从底向上来查找,二叉搜索树就不用了,因为搜索树有序(相当于自带方向),那么只要从上向下遍历就可以了。
此时节点5是不是最近公共祖先? 如果 从节点5继续向左遍历,那么将错过成为q的祖先, 如果从节点5继续向右遍历则错过成为p的祖先。

那么我们可以采用前序遍历(其实这里没有中节点的处理逻辑,遍历顺序无所谓了)。
所以当我们从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。

理解这一点,本题就很好解了。

而递归遍历顺序,本题就不涉及到 前中后序了(这里没有中节点的处理逻辑,遍历顺序无所谓了)。

如图所示:p为节点3,q为节点5

![235.二叉搜索树的最近公共祖先](https://img-blog.csdnimg.cn/20210204150858927.png)
![235.二叉搜索树的最近公共祖先2](https://code-thinking-1253855093.file.myqcloud.com/pics/20220926165141.png)


可以看出直接按照指定的方向,就可以找到节点4,为最近公共祖先,而且不需要遍历整棵树,找到结果直接返回!

Expand Down
52 changes: 31 additions & 21 deletions problems/0236.二叉树的最近公共祖先.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,29 @@

回溯啊,二叉树回溯的过程就是从低到上。

后序遍历就是天然的回溯过程,最先处理的一定是叶子节点。
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。

接下来就看如何判断一个节点是节点q和节点p的公共公共祖先呢。

**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。**
**首先最容易想到的一个情况:如果找到一个节点,发现左子树出现结点p,右子树出现节点q,或者 左子树出现结点q,右子树出现节点p,那么该节点就是节点p和q的最近公共祖先。** 即情况一:

**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。**
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220922173502.png)

使用后序遍历,回溯的过程,就是从低向上遍历节点,一旦发现满足第一种情况的节点,就是最近公共节点了。
判断逻辑是 如果递归遍历遇到q,就将q返回,遇到p 就将p返回,那么如果 左右子树的返回值都不为空,说明此时的中节点,一定是q 和p 的最近祖先。

**但是如果p或者q本身就是最近公共祖先呢**?
那么有录友可能疑惑,会不会左子树 遇到q 返回,右子树也遇到q返回,这样并没有找到 q 和p的最近祖先。

其实只需要找到一个节点是p或者q的时候,直接返回当前节点,无需继续递归子树
这么想的录友,要审题了,题目强调:**二叉树节点数值是不重复的,而且一定存在 q 和 p**

如果接下来的遍历中找到了后继节点满足第一种情况则修改返回值为后继节点,否则,继续返回已找到的节点即可。
**但是很多人容易忽略一个情况,就是节点本身p(q),它拥有一个子孙节点q(p)。** 情况二:

为什么满足第一种情况的节点一定是p或q的后继节点呢?大家可以仔细思考一下。
![](https://code-thinking-1253855093.file.myqcloud.com/pics/20220922173530.png)

其实情况一 和 情况二 代码实现过程都是一样的,也可以说,实现情况一的逻辑,顺便包含了情况二。

因为遇到 q 或者 p 就返回,这样也包含了 q 或者 p 本省就是 公共祖先的情况。

这一点是很多录友容易忽略的,在下面的代码讲解中,可以在去体会。

递归三部曲:

Expand All @@ -69,20 +75,24 @@

代码如下:

```
```CPP
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
```

* 确定终止条件

如果找到了 节点p或者q,或者遇到空节点,就返回。
遇到空的话,然后然后空,因为树都是空了,所以返回空。

那么我们来说一说,如果 root == q,或者 root == p,说明找到 q p ,则将其返回,这个返回值,后面在中节点的处理过程中会用到,那么中节点处理逻辑,后下面讲解。

代码如下:

```
```CPP
if (root == q || root == p || root == NULL) return root;
```



* 确定单层递归逻辑

值得注意的是 本题函数有返回值,是因为回溯的过程需要递归函数的返回值做判断,但本题我们依然要遍历树的所有节点。
Expand All @@ -93,18 +103,18 @@ if (root == q || root == p || root == NULL) return root;

搜索一条边的写法:

```
```CPP
if (递归函数(root->left)) return ;

if (递归函数(root->right)) return ;
```

搜索整个树写法:

```
left = 递归函数(root->left);
right = 递归函数(root->right);
left与right的逻辑处理;
```CPP
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
```

看出区别了没?
Expand All @@ -123,18 +133,18 @@ left与right的逻辑处理;

因为在如下代码的后序遍历中,如果想利用left和right做逻辑处理, 不能立刻返回,而是要等left与right逻辑处理完之后才能返回。

```
left = 递归函数(root->left);
right = 递归函数(root->right);
left与right的逻辑处理;
```CPP
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
```

所以此时大家要知道我们要遍历整棵树。知道这一点,对本题就有一定深度的理解了。


那么先用left和right接住左子树和右子树的返回值,代码如下:

```
```CPP
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);

Expand Down
29 changes: 17 additions & 12 deletions problems/0617.合并二叉树.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,18 @@

注意: 合并必须从两个树的根节点开始。

# 思路
# 视频讲解

**《代码随想录》算法视频公开课:[一起操作两个二叉树?有点懵!| LeetCode:617.合并二叉树](https://www.bilibili.com/video/BV1m14y1Y7JK),相信结合视频在看本篇题解,更有助于大家对本题的理解**。


## 思路

相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?

其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。

## 递归
### 递归

二叉树使用递归,就要想使用前中后哪种遍历方式?

Expand Down Expand Up @@ -207,7 +212,7 @@ public:
};
```

# 拓展
## 拓展

当然也可以秀一波指针的操作,这是我写的野路子,大家就随便看看就行了,以防带跑遍了。

Expand Down Expand Up @@ -239,7 +244,7 @@ public:
};
```

# 总结
## 总结

合并二叉树,也是二叉树操作的经典题目,如果没有接触过的话,其实并不简单,因为我们习惯了操作一个二叉树,一起操作两个二叉树,还会有点懵懵的。

Expand All @@ -250,10 +255,10 @@ public:
最后拓展中,我给了一个操作指针的野路子,大家随便看看就行了,如果学习C++的话,可以在去研究研究。


# 其他语言版本
## 其他语言版本


## Java
### Java

```Java
class Solution {
Expand Down Expand Up @@ -346,7 +351,7 @@ class Solution {
}
```

## Python
### Python

**递归法 - 前序遍历**
```python
Expand Down Expand Up @@ -409,7 +414,7 @@ class Solution:
return root1
```

## Go
### Go

```go
/**
Expand Down Expand Up @@ -503,7 +508,7 @@ func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
}
```

## JavaScript
### JavaScript

> 递归法:

Expand Down Expand Up @@ -583,11 +588,11 @@ var mergeTrees = function(root1, root2) {

```

## TypeScript
### TypeScript

> 递归法:

```type
```typescript
function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | null {
if (root1 === null) return root2;
if (root2 === null) return root1;
Expand Down Expand Up @@ -631,7 +636,7 @@ function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode |
};
```

## Scala
### Scala

递归:
```scala
Expand Down
29 changes: 17 additions & 12 deletions problems/0654.最大二叉树.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@

给定的数组的大小在 [1, 1000] 之间。

# 思路
## 视频讲解

**《代码随想录》算法视频公开课:[又是构造二叉树,又有很多坑!| LeetCode:654.最大二叉树](https://www.bilibili.com/video/BV1MG411G7ox),相信结合视频在看本篇题解,更有助于大家对本题的理解**。


## 思路

最大二叉树的构建过程如下:

Expand Down Expand Up @@ -175,7 +180,7 @@ public:
};
```

# 拓展
## 拓展

可以发现上面的代码看上去简洁一些,**主要是因为第二版其实是允许空节点进入递归,所以不用在递归的时候加判断节点是否为空**

Expand Down Expand Up @@ -207,7 +212,7 @@ root->right = traversal(nums, maxValueIndex + 1, right);

第二版相应的终止条件,是遇到空节点,也就是数组区间为0,就终止了。

# 总结
## 总结


这道题目其实和 [二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 是一个思路,比[二叉树:构造二叉树登场!](https://programmercarl.com/0106.从中序与后序遍历序列构造二叉树.html) 还简单一些。
Expand All @@ -218,10 +223,10 @@ root->right = traversal(nums, maxValueIndex + 1, right);

其实就是不同代码风格的实现,**一般情况来说:如果让空节点(空指针)进入递归,就不加if,如果不让空节点进入递归,就加if限制一下, 终止条件也会相应的调整。**

# 其他语言版本
## 其他语言版本


## Java
### Java

```Java
class Solution {
Expand Down Expand Up @@ -253,7 +258,7 @@ class Solution {
}
```

## Python
### Python

```python
class Solution:
Expand Down Expand Up @@ -300,7 +305,7 @@ class Solution:
return root
```

## Go
### Go


```go
Expand Down Expand Up @@ -334,7 +339,7 @@ func findMax(nums []int) (index int){
}
```

## JavaScript
### JavaScript

```javascript
/**
Expand Down Expand Up @@ -371,7 +376,7 @@ var constructMaximumBinaryTree = function (nums) {
};
```

## TypeScript
### TypeScript

> 新建数组法:

Expand Down Expand Up @@ -419,7 +424,7 @@ function constructMaximumBinaryTree(nums: number[]): TreeNode | null {



## C
### C

```c
struct TreeNode* traversal(int* nums, int left, int right) {
Expand Down Expand Up @@ -450,7 +455,7 @@ struct TreeNode* constructMaximumBinaryTree(int* nums, int numsSize){
}
```

## Swift
### Swift
```swift
func constructMaximumBinaryTree(_ nums: inout [Int]) -> TreeNode? {
return traversal(&nums, 0, nums.count)
Expand All @@ -476,7 +481,7 @@ func traversal(_ nums: inout [Int], _ left: Int, _ right: Int) -> TreeNode? {
}
```

## Scala
### Scala

```scala
object Solution {
Expand Down
Loading