leetcode-master/problems/0617.合并二叉树.md

594 lines
19 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<p align="center">
<a href="https://mp.weixin.qq.com/s/RsdcQ9umo09R6cfnwXZlrQ"><img src="https://img.shields.io/badge/PDF下载-代码随想录-blueviolet" alt=""></a>
<a href="https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw"><img src="https://img.shields.io/badge/刷题-微信群-green" alt=""></a>
<a href="https://space.bilibili.com/525438321"><img src="https://img.shields.io/badge/B站-代码随想录-orange" alt=""></a>
<a href="https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ"><img src="https://img.shields.io/badge/知识星球-代码随想录-blue" alt=""></a>
</p>
<p align="center"><strong>欢迎大家<a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 617.合并二叉树
[力扣题目链接](https://leetcode-cn.com/problems/merge-two-binary-trees/)
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠那么将他们的值相加作为节点合并后的新值否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
![617.合并二叉树](https://img-blog.csdnimg.cn/20210204153634809.png)
注意: 合并必须从两个树的根节点开始。
# 思路
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?
其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。
## 递归
二叉树使用递归,就要想使用前中后哪种遍历方式?
**本题使用哪种遍历都是可以的!**
我们下面以前序遍历为例。
动画如下:
![617.合并二叉树](https://tva1.sinaimg.cn/large/008eGmZEly1gnbjjq8h16g30e20cwnpd.gif)
那么我们来按照递归三部曲来解决:
1. **确定递归函数的参数和返回值:**
首先那么要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
代码如下:
```
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
```
2. **确定终止条件:**
因为是传入了两个树那么就有两个树遍历的节点t1 和 t2如果t1 == NULL 了,两个树合并就应该是 t2 了啊如果t2也为NULL也无所谓合并之后就是NULL
反过来如果t2 == NULL那么两个数合并就是t1如果t1也为NULL也无所谓合并之后就是NULL
代码如下:
```
if (t1 == NULL) return t2; // 如果t1为空合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空合并之后就应该是t1
```
3. **确定单层递归的逻辑:**
单层递归的逻辑就比较好些了这里我们用重复利用一下t1这个树t1就是合并之后树的根节点就是修改了原来树的结构
那么单层递归中,就要把两棵树的元素加到一起。
```
t1->val += t2->val;
```
接下来t1 的左子树是:合并 t1左子树 t2左子树之后的左子树。
t1 的右子树:是 合并 t1右子树 t2右子树之后的右子树。
最终t1就是合并之后的根节点。
代码如下:
```
t1->left = mergeTrees(t1->left, t2->left);
t1->right = mergeTrees(t1->right, t2->right);
return t1;
```
此时前序遍历,完整代码就写出来了,如下:
```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2; // 如果t1为空合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空合并之后就应该是t1
// 修改了t1的数值和结构
t1->val += t2->val; // 中
t1->left = mergeTrees(t1->left, t2->left); // 左
t1->right = mergeTrees(t1->right, t2->right); // 右
return t1;
}
};
```
那么中序遍历也是可以的,代码如下:
```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2; // 如果t1为空合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空合并之后就应该是t1
// 修改了t1的数值和结构
t1->left = mergeTrees(t1->left, t2->left); // 左
t1->val += t2->val; // 中
t1->right = mergeTrees(t1->right, t2->right); // 右
return t1;
}
};
```
后序遍历依然可以,代码如下:
```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2; // 如果t1为空合并之后就应该是t2
if (t2 == NULL) return t1; // 如果t2为空合并之后就应该是t1
// 修改了t1的数值和结构
t1->left = mergeTrees(t1->left, t2->left); // 左
t1->right = mergeTrees(t1->right, t2->right); // 右
t1->val += t2->val; // 中
return t1;
}
};
```
**但是前序遍历是最好理解的我建议大家用前序遍历来做就OK。**
如上的方法修改了t1的结构当然也可以不修改t1和t2的结构重新定一个树。
不修改输入树的结构,前序遍历,代码如下:
```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2;
if (t2 == NULL) return t1;
// 重新定义新的节点,不修改原有两个树的结构
TreeNode* root = new TreeNode(0);
root->val = t1->val + t2->val;
root->left = mergeTrees(t1->left, t2->left);
root->right = mergeTrees(t1->right, t2->right);
return root;
}
};
```
## 迭代法
使用迭代法,如何同时处理两棵树呢?
思路我们在[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html)中的迭代法已经讲过一次了,求二叉树对称的时候就是把两个树的节点同时加入队列进行比较。
本题我们也使用队列,模拟的层序遍历,代码如下:
```CPP
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (t1 == NULL) return t2;
if (t2 == NULL) return t1;
queue<TreeNode*> que;
que.push(t1);
que.push(t2);
while(!que.empty()) {
TreeNode* node1 = que.front(); que.pop();
TreeNode* node2 = que.front(); que.pop();
// 此时两个节点一定不为空val相加
node1->val += node2->val;
// 如果两棵树左节点都不为空,加入队列
if (node1->left != NULL && node2->left != NULL) {
que.push(node1->left);
que.push(node2->left);
}
// 如果两棵树右节点都不为空,加入队列
if (node1->right != NULL && node2->right != NULL) {
que.push(node1->right);
que.push(node2->right);
}
// 当t1的左节点 为空 t2左节点不为空就赋值过去
if (node1->left == NULL && node2->left != NULL) {
node1->left = node2->left;
}
// 当t1的右节点 为空 t2右节点不为空就赋值过去
if (node1->right == NULL && node2->right != NULL) {
node1->right = node2->right;
}
}
return t1;
}
};
```
# 拓展
当然也可以秀一波指针的操作,这是我写的野路子,大家就随便看看就行了,以防带跑遍了。
如下代码中,想要更改二叉树的值,应该传入指向指针的指针。
代码如下:(前序遍历)
```CPP
class Solution {
public:
void process(TreeNode** t1, TreeNode** t2) {
if ((*t1) == NULL && (*t2) == NULL) return;
if ((*t1) != NULL && (*t2) != NULL) {
(*t1)->val += (*t2)->val;
}
if ((*t1) == NULL && (*t2) != NULL) {
*t1 = *t2;
return;
}
if ((*t1) != NULL && (*t2) == NULL) {
return;
}
process(&((*t1)->left), &((*t2)->left));
process(&((*t1)->right), &((*t2)->right));
}
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
process(&t1, &t2);
return t1;
}
};
```
# 总结
合并二叉树,也是二叉树操作的经典题目,如果没有接触过的话,其实并不简单,因为我们习惯了操作一个二叉树,一起操作两个二叉树,还会有点懵懵的。
这不是我们第一次操作两颗二叉树了,在[二叉树:我对称么?](https://programmercarl.com/0101.对称二叉树.html)中也一起操作了两棵二叉树。
迭代法中,一般一起操作两个树都是使用队列模拟类似层序遍历,同时处理两个树的节点,这种方式最好理解,如果用模拟递归的思路的话,要复杂一些。
最后拓展中我给了一个操作指针的野路子大家随便看看就行了如果学习C++的话,可以在去研究研究。
# 其他语言版本
## Java
```Java
class Solution {
// 递归
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 == null) return root1;
TreeNode newRoot = new TreeNode(root1.val + root2.val);
newRoot.left = mergeTrees(root1.left,root2.left);
newRoot.right = mergeTrees(root1.right,root2.right);
return newRoot;
}
}
```
```Java
class Solution {
// 使用栈迭代
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root2);
stack.push(root1);
while (!stack.isEmpty()) {
TreeNode node1 = stack.pop();
TreeNode node2 = stack.pop();
node1.val += node2.val;
if (node2.right != null && node1.right != null) {
stack.push(node2.right);
stack.push(node1.right);
} else {
if (node1.right == null) {
node1.right = node2.right;
}
}
if (node2.left != null && node1.left != null) {
stack.push(node2.left);
stack.push(node1.left);
} else {
if (node1.left == null) {
node1.left = node2.left;
}
}
}
return root1;
}
}
```
```java
class Solution {
// 使用队列迭代
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 ==null) return root1;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root1);
queue.offer(root2);
while (!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
// 此时两个节点一定不为空val相加
node1.val = node1.val + node2.val;
// 如果两棵树左节点都不为空,加入队列
if (node1.left != null && node2.left != null) {
queue.offer(node1.left);
queue.offer(node2.left);
}
// 如果两棵树右节点都不为空,加入队列
if (node1.right != null && node2.right != null) {
queue.offer(node1.right);
queue.offer(node2.right);
}
// 若node1的左节点为空直接赋值
if (node1.left == null && node2.left != null) {
node1.left = node2.left;
}
// 若node2的左节点为空直接赋值
if (node1.right == null && node2.right != null) {
node1.right = node2.right;
}
}
return root1;
}
}
```
## Python
**递归法 - 前序遍历**
```python
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
# 递归终止条件:
# 但凡有一个节点为空, 就立刻返回另外一个. 如果另外一个也为None就直接返回None.
if not root1:
return root2
if not root2:
return root1
# 上面的递归终止条件保证了代码执行到这里root1, root2都非空.
root1.val += root2.val # 中
root1.left = self.mergeTrees(root1.left, root2.left) #左
root1.right = self.mergeTrees(root1.right, root2.right) # 右
return root1 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间.
```
**迭代法**
```python
class Solution:
def mergeTrees(self, root1: TreeNode, root2: TreeNode) -> TreeNode:
if not root1:
return root2
if not root2:
return root1
queue = deque()
queue.append(root1)
queue.append(root2)
while queue:
node1 = queue.popleft()
node2 = queue.popleft()
# 更新queue
# 只有两个节点都有左节点时, 再往queue里面放.
if node1.left and node2.left:
queue.append(node1.left)
queue.append(node2.left)
# 只有两个节点都有右节点时, 再往queue里面放.
if node1.right and node2.right:
queue.append(node1.right)
queue.append(node2.right)
# 更新当前节点. 同时改变当前节点的左右孩子.
node1.val += node2.val
if not node1.left and node2.left:
node1.left = node2.left
if not node1.right and node2.right:
node1.right = node2.right
return root1
```
## Go
```go
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
//前序遍历递归遍历跟105 106差不多的思路
func mergeTrees(t1 *TreeNode, t2 *TreeNode) *TreeNode {
var value int
var nullNode *TreeNode//空node便于遍历
nullNode=&TreeNode{
Val:0,
Left:nil,
Right:nil}
switch {
case t1==nil&&t2==nil: return nil//终止条件
default : //如果其中一个节点为空则将该节点置为nullNode方便下次遍历
if t1==nil{
value=t2.Val
t1=nullNode
}else if t2==nil{
value=t1.Val
t2=nullNode
}else {
value=t1.Val+t2.Val
}
}
root:=&TreeNode{//构造新的二叉树
Val: value,
Left: mergeTrees(t1.Left,t2.Left),
Right: mergeTrees(t1.Right,t2.Right)}
return root
}
// 前序遍历简洁版
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
if root1 == nil {
return root2
}
if root2 == nil {
return root1
}
root1.Val += root2.Val
root1.Left = mergeTrees(root1.Left, root2.Left)
root1.Right = mergeTrees(root1.Right, root2.Right)
return root1
}
// 迭代版本
func mergeTrees(root1 *TreeNode, root2 *TreeNode) *TreeNode {
queue := make([]*TreeNode,0)
if root1 == nil{
return root2
}
if root2 == nil{
return root1
}
queue = append(queue,root1)
queue = append(queue,root2)
for size:=len(queue);size>0;size=len(queue){
node1 := queue[0]
queue = queue[1:]
node2 := queue[0]
queue = queue[1:]
node1.Val += node2.Val
// 左子树都不为空
if node1.Left != nil && node2.Left != nil{
queue = append(queue,node1.Left)
queue = append(queue,node2.Left)
}
// 右子树都不为空
if node1.Right !=nil && node2.Right !=nil{
queue = append(queue,node1.Right)
queue = append(queue,node2.Right)
}
// 树 1 的左子树为 nil直接接上树 2 的左子树
if node1.Left == nil{
node1.Left = node2.Left
}
// 树 1 的右子树为 nil直接接上树 2 的右子树
if node1.Right == nil{
node1.Right = node2.Right
}
}
return root1
}
```
## JavaScript
> 递归法:
```javascript
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root1
* @param {TreeNode} root2
* @return {TreeNode}
*/
var mergeTrees = function (root1, root2) {
const preOrder = (root1, root2) => {
if (!root1)
return root2
if (!root2)
return root1;
root1.val += root2.val;
root1.left = preOrder(root1.left, root2.left);
root1.right = preOrder(root1.right, root2.right);
return root1;
}
return preOrder(root1, root2);
};
```
> 迭代法:
```javascript
/**
* Definition for a binary tree node.
* function TreeNode(val, left, right) {
* this.val = (val===undefined ? 0 : val)
* this.left = (left===undefined ? null : left)
* this.right = (right===undefined ? null : right)
* }
*/
/**
* @param {TreeNode} root1
* @param {TreeNode} root2
* @return {TreeNode}
*/
var mergeTrees = function(root1, root2) {
if (root1 === null) return root2;
if (root2 === null) return root1;
let queue = [];
queue.push(root1);
queue.push(root2);
while (queue.length) {
let node1 = queue.shift();
let node2 = queue.shift();;
node1.val += node2.val;
if (node1.left !== null && node2.left !== null) {
queue.push(node1.left);
queue.push(node2.left);
}
if (node1.right !== null && node2.right !== null) {
queue.push(node1.right);
queue.push(node2.right);
}
if (node1.left === null && node2.left !== null) {
node1.left = node2.left;
}
if (node1.right === null && node2.right !== null) {
node1.right = node2.right;
}
}
return root1;
};
```
-----------------------
* 作者微信:[程序员Carl](https://mp.weixin.qq.com/s/b66DFkOp8OOxdZC_xLZxfw)
* B站视频[代码随想录](https://space.bilibili.com/525438321)
* 知识星球:[代码随想录](https://mp.weixin.qq.com/s/QVF6upVMSbgvZy8lHZS3CQ)
<div align="center"><img src=https://code-thinking.cdn.bcebos.com/pics/01二维码一.jpg width=500> </img></div>