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

235 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

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.

## 题目地址
https://leetcode-cn.com/problems/merge-two-binary-trees/
## 思路
相信这道题目很多同学疑惑的点是如何同时遍历两个二叉树呢?
其实和遍历一个树逻辑是一样的,只不过传入两个树的节点,同时操作。
那么前中后序应该使用哪种遍历呢?
**本题使用哪种遍历都是可以的!**
我们下面以前序遍历为例。
动画如下:
<img src='../video/617.合并二叉树.gif' width=600> </img></div>
那么我们来按照递归三部曲来解决:
1. **确定递归函数的参数和返回值:**
首先那么要合入两个二叉树,那么参数至少是要传入两个二叉树的根节点,返回值就是合并之后二叉树的根节点。
代码如下:
```
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
```
2. **确定终止条件:**
因为是传入了两个树那么就有两个树遍历的节点t1 和 t2如果t1 == NULL 了,两个树合并就应该是 t2 了啊如果t2也为NULL也无所谓
反过来如果t2 == NULL那么两个数合并就是t1如果t1也为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->left = mergeTrees(t1->left, t2->left);
t1->right = mergeTrees(t1->right, t2->right);
return t1;
```
此时前序遍历,修改原输入树结构的完整代码就写出来了,如下:
```
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;
}
};
```
那么中序遍历可不可以呢,也是可以的,代码如下:
```
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;
}
};
```
后序遍历呢,依然可以,代码如下:
```
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。**
**那么如下还总结了四种方法,递归的方式均使用了前序遍历,此时大家应该知道了,以下每一种递归的方法都可以换成中序和后序遍历,所以本题的解法是很多的。**
**其实这道题目迭代法实现是比较困难的,大家可以试一试,是一道不错的面试进阶题目。**
四种写法如下:
1. 递归修改了输入树的结构
2. 递归不修改树的结构
3. 递归一波指针的操作自己写的野路子可以用来深度理解一下C++的指针)
4. 迭代(这应该是最简单直观的迭代法代码了,一看就懂)
## C++代码
### 递归
修改了输入树的结构,前序遍历
```
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;
}
};
```
不修改输入树的结构,前序遍历
```
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;
}
};
```
一波指针的操作,自己写的野路子
想要更改二叉树的值,应该传入指向指针的指针, 如果process(t1, t2);这么写的话其实只是传入的一个int型的指针并没有传入地址要传入指向指针的指针才能完成对t1的修改。
(前序遍历)
```
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;
}
};
```
### 迭代
这应该是最简单直观的迭代法了,模拟的层序遍历。
```
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;
}
};
```
> 更多算法干货文章持续更新可以微信搜索「代码随想录」第一时间围观关注后回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等就可以获得我多年整理的学习资料。