文章目录
- 题目
- 标题和出处
- 难度
- 题目描述
- 要求
- 示例
- 数据范围
- 解法一
- 思路和算法
- 代码
- 复杂度分析
- 解法二
- 思路和算法
- 代码
- 复杂度分析
题目
标题和出处
标题:合并二叉树
出处:617. 合并二叉树
难度
3 级
题目描述
要求
给定两个二叉树 root1 \texttt{root1} root1 和 root2 \texttt{root2} root2。
想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些结点会重叠,另一些结点不会重叠。你需要将它们合并为一个新的二叉树。合并的规则是如果两个结点重叠,那么将它们的值相加作为结点合并后的新值,否则不为空的结点将直接作为新二叉树的结点。
返回合并后的二叉树。
注意:合并必须从两个树的根结点开始。
示例
示例 1:
输入:
root1
=
[1,3,2,5],
root2
=
[2,1,3,null,4,null,7]
\texttt{root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]}
root1 = [1,3,2,5], root2 = [2,1,3,null,4,null,7]
输出:
[3,4,5,5,4,null,7]
\texttt{[3,4,5,5,4,null,7]}
[3,4,5,5,4,null,7]
示例 2:
输入:
root1
=
[1],
root2
=
[1,2]
\texttt{root1 = [1], root2 = [1,2]}
root1 = [1], root2 = [1,2]
输出:
[2,2]
\texttt{[2,2]}
[2,2]
数据范围
- 树中结点数目在范围 [0, 2000] \texttt{[0, 2000]} [0, 2000] 内
- -10 4 ≤ Node.val < 10 4 \texttt{-10}^\texttt{4} \le \texttt{Node.val} < \texttt{10}^\texttt{4} -104≤Node.val<104
解法一
思路和算法
在合并两个二叉树之前,首先需要判断两个二叉树是否为空。如果两个二叉树都为空,则合之后的二叉树也为空;如果两个二叉树有一个为空,则合并后的二叉树为另一个非空二叉树。
当两个二叉树都不为空时,需要对两个二叉树的相同位置的结点分别合并。合并后的二叉树的根结点值为两个二叉树的根结点值之和,合并后的二叉树的根结点的左子树为两个二叉树的根结点的左子树合并后的结果,合并后的二叉树的根结点的右子树为两个二叉树的根结点的右子树合并后的结果。
合并二叉树的过程是一个递归的过程,递归的终止条件是当前的两个二叉树中至少有一个为空。当两个二叉树都不为空时,首先计算合并后的二叉树的根结点值,然后对两个二叉树的根结点的左子树和右子树分别执行合并操作并作为合并后的二叉树的根结点的左子树和右子树,即可完成二叉树的合并。
代码
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
TreeNode merged = new TreeNode(root1.val + root2.val);
merged.left = mergeTrees(root1.left, root2.left);
merged.right = mergeTrees(root1.right, root2.right);
return merged;
}
}
复杂度分析
-
时间复杂度: O ( min ( m , n ) ) O(\min(m, n)) O(min(m,n)),其中 m m m 和 n n n 分别是两个二叉树的结点数。同时遍历两个二叉树,只有当两个二叉树在相同位置的结点都不为空时才会对该位置的结点执行显性合并操作,因此执行显性合并操作的结点数不超过较小的二叉树的结点数。
-
空间复杂度: O ( min ( m , n ) ) O(\min(m, n)) O(min(m,n)),其中 m m m 和 n n n 分别是两个二叉树的结点数。空间复杂度主要是递归调用的栈空间,栈空间不超过较小的二叉树的高度,最坏情况下二叉树的高度和结点数相等。
解法二
思路和算法
也可以使用广度优先搜索合并二叉树。同样首先判断两个二叉树是否为空,如果两个二叉树至少有一个为空,则可以直接得到合并后的二叉树。当两个二叉树都不为空时,合并后的二叉树的根结点值为两个二叉树的根结点值之和,从合并后的二叉树和两个原始二叉树的根结点开始同时广度优先搜索。
使用三个队列分别存储合并后的二叉树的结点、第一个二叉树的结点、第二个二叉树的结点。初始时将三个根结点分别入三个队列,遍历过程中,每次从三个队列中分别将一个结点出队列,并获得两个原始二叉树的当前结点的左子结点和右子结点,以下将合并后的二叉树的当前结点称为合并结点,将原始二叉树的当前结点称为原始结点,执行合并操作。
以下为左子结点的合并操作。
-
如果两个原始结点的左子结点都不为空,则合并结点的左子结点值为两个原始结点的左子结点值之和,将合并结点的左子结点和两个原始结点的左子结点分别入三个队列。
-
如果两个原始结点的左子结点只有一个不为空,则将两个原始结点中的非空的左子结点作为合并结点的左子结点,此时合并结点的左子树即为两个原始结点中的非空的左子树,不需要对非空的左子树继续遍历,因此不需要将左子结点入队列。
-
如果两个原始结点的左子结点都为空,则合并结点的左子结点也为空。
对于右子结点,使用相同的操作执行合并。
当队列为空时遍历结束,此时二叉树合并完毕。
代码
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) {
return root2;
}
if (root2 == null) {
return root1;
}
TreeNode merged = new TreeNode(root1.val + root2.val);
Queue<TreeNode> queue = new ArrayDeque<TreeNode>();
Queue<TreeNode> queue1 = new ArrayDeque<TreeNode>();
Queue<TreeNode> queue2 = new ArrayDeque<TreeNode>();
queue.offer(merged);
queue1.offer(root1);
queue2.offer(root2);
while (!queue.isEmpty()) {
TreeNode node = queue.poll(), node1 = queue1.poll(), node2 = queue2.poll();
TreeNode left1 = node1.left, right1 = node1.right, left2 = node2.left, right2 = node2.right;
if (left1 != null || left2 != null) {
if (left1 != null && left2 != null) {
TreeNode left = new TreeNode(left1.val + left2.val);
node.left = left;
queue.offer(left);
queue1.offer(left1);
queue2.offer(left2);
} else if (left1 != null) {
node.left = left1;
} else {
node.left = left2;
}
}
if (right1 != null || right2 != null) {
if (right1 != null && right2 != null) {
TreeNode right = new TreeNode(right1.val + right2.val);
node.right = right;
queue.offer(right);
queue1.offer(right1);
queue2.offer(right2);
} else if (right1 != null) {
node.right = right1;
} else {
node.right = right2;
}
}
}
return merged;
}
}
复杂度分析
-
时间复杂度: O ( min ( m , n ) ) O(\min(m, n)) O(min(m,n)),其中 m m m 和 n n n 分别是两个二叉树的结点数。同时遍历两个二叉树,只有当两个二叉树在相同位置的结点都不为空时才会访问该位置的结点,因此访问的结点数不超过较小的二叉树的结点数。
-
空间复杂度: O ( min ( m , n ) ) O(\min(m, n)) O(min(m,n)),其中 m m m 和 n n n 分别是两个二叉树的结点数。空间复杂度主要是队列空间,队列内元素个数不超过较小的二叉树的结点数。