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

797 lines
24 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://programmercarl.com/other/xunlianying.html" target="_blank">
<img src="../pics/训练营.png" width="1000"/>
</a>
<p align="center"><strong><a href="https://mp.weixin.qq.com/s/tqCxrMEU-ajQumL1i8im9A">参与本项目</a>,贡献其他语言版本的代码,拥抱开源,让更多学习算法的小伙伴们收益!</strong></p>
# 617.合并二叉树
[力扣题目链接](https://leetcode.cn/problems/merge-two-binary-trees/)
给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。
你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠那么将他们的值相加作为节点合并后的新值否则不为 NULL 的节点将直接作为新二叉树的节点。
示例 1:
![617.合并二叉树](https://code-thinking-1253855093.file.myqcloud.com/pics/20230310000854.png)
注意: 合并必须从两个树的根节点开始。
## 算法公开课
**[《代码随想录》算法视频公开课](https://programmercarl.com/other/gongkaike.html)[一起操作两个二叉树?有点懵!| LeetCode617.合并二叉树](https://www.bilibili.com/video/BV1m14y1Y7JK),相信结合视频在看本篇题解,更有助于大家对本题的理解**。
## 思路
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?
其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。
### 递归
二叉树使用递归,就要想使用前中后哪种遍历方式?
**本题使用哪种遍历都是可以的!**
我们下面以前序遍历为例。
动画如下:
![617.合并二叉树](https://code-thinking.cdn.bcebos.com/gifs/617.%E5%90%88%E5%B9%B6%E4%BA%8C%E5%8F%89%E6%A0%91.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;
root1.val += root2.val;
root1.left = mergeTrees(root1.left,root2.left);
root1.right = mergeTrees(root1.right,root2.right);
return root1;
}
}
```
```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;
}
// 若node1的右节点为空直接赋值
if (node1.right == null && node2.right != null) {
node1.right = node2.right;
}
}
return root1;
}
}
```
### Python
(版本一) 递归 - 前序 - 修改root1
```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 # ⚠️ 注意: 本题我们重复使用了题目给出的节点而不是创建新节点. 节省时间, 空间.
```
(版本二) 递归 - 前序 - 新建root
```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都非空.
root = TreeNode() # 创建新节点
root.val += root1.val + root2.val# 中
root.left = self.mergeTrees(root1.left, root2.left) #左
root.right = self.mergeTrees(root1.right, root2.right) # 右
return root # ⚠️ 注意: 本题我们创建了新节点.
```
(版本三) 迭代
```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
```
(版本四) 迭代 + 代码优化
```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
from collections import deque
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, root2))
while queue:
node1, node2 = queue.popleft()
node1.val += node2.val
if node1.left and node2.left:
queue.append((node1.left, node2.left))
elif not node1.left:
node1.left = node2.left
if node1.right and node2.right:
queue.append((node1.right, node2.right))
elif not node1.right:
node1.right = node2.right
return root1
```
### Go
```go
// 前序遍历
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;
};
```
### TypeScript
> 递归法:
```typescript
function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | null {
if (root1 === null) return root2;
if (root2 === null) return root1;
const resNode: TreeNode = new TreeNode(root1.val + root2.val);
resNode.left = mergeTrees(root1.left, root2.left);
resNode.right = mergeTrees(root1.right, root2.right);
return resNode;
};
```
> 迭代法:
```typescript
function mergeTrees(root1: TreeNode | null, root2: TreeNode | null): TreeNode | null {
if (root1 === null) return root2;
if (root2 === null) return root1;
const helperQueue1: TreeNode[] = [],
helperQueue2: TreeNode[] = [];
helperQueue1.push(root1);
helperQueue2.push(root2);
let tempNode1: TreeNode,
tempNode2: TreeNode;
while (helperQueue1.length > 0) {
tempNode1 = helperQueue1.shift()!;
tempNode2 = helperQueue2.shift()!;
tempNode1.val += tempNode2.val;
if (tempNode1.left !== null && tempNode2.left !== null) {
helperQueue1.push(tempNode1.left);
helperQueue2.push(tempNode2.left);
} else if (tempNode1.left === null) {
tempNode1.left = tempNode2.left;
}
if (tempNode1.right !== null && tempNode2.right !== null) {
helperQueue1.push(tempNode1.right);
helperQueue2.push(tempNode2.right);
} else if (tempNode1.right === null) {
tempNode1.right = tempNode2.right;
}
}
return root1;
};
```
### Scala
递归:
```scala
object Solution {
def mergeTrees(root1: TreeNode, root2: TreeNode): TreeNode = {
if (root1 == null) return root2 // 如果root1为空返回root2
if (root2 == null) return root1 // 如果root2为空返回root1
// 新建一个节点,值为两个节点的和
var node = new TreeNode(root1.value + root2.value)
// 往下递归
node.left = mergeTrees(root1.left, root2.left)
node.right = mergeTrees(root1.right, root2.right)
node // 返回nodereturn关键字可以省略
}
}
```
迭代:
```scala
object Solution {
import scala.collection.mutable
def mergeTrees(root1: TreeNode, root2: TreeNode): TreeNode = {
if (root1 == null) return root2
if (root2 == null) return root1
var stack = mutable.Stack[TreeNode]()
// 先放node2再放node1
stack.push(root2)
stack.push(root1)
while (!stack.isEmpty) {
var node1 = stack.pop()
var node2 = stack.pop()
node1.value += node2.value
if (node1.right != null && node2.right != null) {
stack.push(node2.right)
stack.push(node1.right)
} else {
if(node1.right == null){
node1.right = node2.right
}
}
if (node1.left != null && node2.left != null) {
stack.push(node2.left)
stack.push(node1.left)
} else {
if(node1.left == null){
node1.left = node2.left
}
}
}
root1
}
}
```
### Rust
递归:
```rust
use std::cell::RefCell;
use std::rc::Rc;
impl Solution {
pub fn merge_trees(
root1: Option<Rc<RefCell<TreeNode>>>,
root2: Option<Rc<RefCell<TreeNode>>>,
) -> Option<Rc<RefCell<TreeNode>>> {
if root1.is_none() {
return root2;
}
if root2.is_none() {
return root1;
}
let binding = root1.clone();
let mut node1 = binding.as_ref().unwrap().borrow_mut();
let node2 = root2.as_ref().unwrap().borrow_mut();
node1.left = Self::merge_trees(node1.left.clone(), node2.left.clone());
node1.right = Self::merge_trees(node1.right.clone(), node2.right.clone());
node1.val += node2.val;
root1
}
}
```
迭代:
```rust
impl Solution {
pub fn merge_trees(
root1: Option<Rc<RefCell<TreeNode>>>,
root2: Option<Rc<RefCell<TreeNode>>>,
) -> Option<Rc<RefCell<TreeNode>>> {
if root1.is_none() {
return root2;
}
if root2.is_none() {
return root1;
}
let mut stack = vec![];
stack.push(root2);
stack.push(root1.clone());
while !stack.is_empty() {
let node1 = stack.pop().unwrap().unwrap();
let node2 = stack.pop().unwrap().unwrap();
let mut node1 = node1.borrow_mut();
let node2 = node2.borrow();
node1.val += node2.val;
if node1.left.is_some() && node2.left.is_some() {
stack.push(node2.left.clone());
stack.push(node1.left.clone());
}
if node1.right.is_some() && node2.right.is_some() {
stack.push(node2.right.clone());
stack.push(node1.right.clone());
}
if node1.left.is_none() && node2.left.is_some() {
node1.left = node2.left.clone();
}
if node1.right.is_none() && node2.right.is_some() {
node1.right = node2.right.clone();
}
}
root1
}
}
```
<p align="center">
<a href="https://programmercarl.com/other/kstar.html" target="_blank">
<img src="../pics/网站星球宣传海报.jpg" width="1000"/>
</a>