牛客题解 | 合并二叉树

题目

题目链接

题目的主要信息:
  • 合并(相加)二叉树位置相同的节点
  • 缺少的节点用另一棵树来补,若都缺则返回NULL
举一反三:

学习完本题的思路你可以解决如下题目:

BM28. 二叉树的最大深度

BM29. 二叉树中和为某一值的路径(一)

BM31. 对称的二叉树

BM33. 二叉树的镜像

BM36. 判断是不是平衡二叉树

BM39. 序列化二叉树

BM41. 输出二叉树的右视图

方法一:递归前序遍历(推荐使用)

知识点:二叉树递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

思路:

要将一棵二叉树的节点与另一棵二叉树相加合并,肯定需要遍历两棵二叉树,那我们可以考虑同步遍历两棵二叉树,这样就可以将每次遍历到的值相加在一起。遍历的方式有多种,这里推荐前序递归遍历。

具体做法:

  • step 1:首先判断t1与t2是否为空,若为则用另一个代替,若都为空,返回的值也是空。
  • step 2:然后依据前序遍历的特点,优先访问根节点,将两个根点的值相加创建到新树中。
  • step 3:两棵树再依次同步进入左子树和右子树。

Java实现代码:

import java.util.*;
public class Solution {
    public TreeNode mergeTrees (TreeNode t1, TreeNode t2) {
        //若只有一个节点返回另一个,两个都为null自然返回null
        if (t1 == null) 
            return t2;
        if (t2 == null)
            return t1;
        //根左右的方式递归
        TreeNode head = new TreeNode(t1.val + t2.val);
        head.left = mergeTrees(t1.left, t2.left);
        head.right = mergeTrees(t1.right, t2.right);
        return head;
    }
}

C++实现代码:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        //若只有一个节点返回另一个,两个都为NULL自然返回NULL
        if (t1 == NULL) 
            return t2;
        if (t2 == NULL)
            return t1;
        //根左右的方式递归
        TreeNode* head = new TreeNode(t1->val + t2->val);
        head->left = mergeTrees(t1->left, t2->left);
        head->right = mergeTrees(t1->right, t2->right);
        return head;
    }
};

Python实现代码

class Solution:
    def mergeTrees(self , t1: TreeNode, t2: TreeNode) -> TreeNode:
        # 若只有一个节点返回另一个,两个都为NULL自然返回NULL
        if not t1: 
            return t2
        if not t2:
            return t1
        # 根左右的方式递归
        head = TreeNode(t1.val + t2.val)
        head.left = self.mergeTrees(t1.left, t2.left)
        head.right = self.mergeTrees(t1.right, t2.right)
        return head

复杂度分析:

  • 时间复杂度: O ( m i n ( n , m ) ) O(min(n,m)) O(min(n,m)),m和n分别为两棵树的节点树,当一个树访问完时,自然就连接上另一个树的节点,故只访问了小树的节点数。
  • 空间复杂度: O ( m i n ( n , m ) ) O(min(n,m)) O(min(n,m)),递归栈深度也同时间,只访问了小树的节点数。
方法二:非递归层次遍历(扩展思路)

知识点:队列

队列是一种仅支持在表尾进行插入操作、在表头进行删除操作的线性表,插入端称为队尾,删除端称为队首,因整体类似排队的队伍而得名。它满足先进先出的性质,元素入队即将新元素加在队列的尾,元素出队即将队首元素取出,它后一个作为新的队首。

思路:

除了递归的遍历以外,非递归的层次遍历,也可以实现两棵树同步遍历节点相加,重点是两棵树从根节点开始每个节点是同步走的,因此我们可以使用队列辅助两个二叉树分别同时层次遍历。

具体做法:

  • step 1:首先判断t1与t2是否为空,若为则用另一个代替,若都为空,返回的值也是空。
  • step 2:使用三个辅助队列,第一个队列q用于暂存合并后的二叉树的层次遍历节点,第二个队列q1用于暂存t1的层次遍历节点,第三个队列q2用于暂存t2的层次遍历节点。
  • step 3:两棵树同步层次遍历,先将根节点加入队列中,同时根节点优先合并。
  • step 4:每次从队列分别弹出一个元素,判断分别二者的左右子节点是否存在,若是都存在,则相加合并,若是只存在一个则连接该存在的节点,若是都不存在则连接null。

图示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java实现代码:

import java.util.*;
public class Solution {
    public TreeNode mergeTrees (TreeNode t1, TreeNode t2) {
        //若只有一个节点返回另一个,两个都为null自然返回null
        if (t1 == null)
            return t2;
        if (t2 == null)
            return t1;
        //合并根节点
        TreeNode head = new TreeNode(t1.val + t2.val); 
        //连接后的树的层次遍历节点
        Queue<TreeNode> q = new LinkedList<TreeNode>(); 
        //分别存两棵树的层次遍历节点
        Queue<TreeNode> q1 = new LinkedList<TreeNode>(); 
        Queue<TreeNode> q2 = new LinkedList<TreeNode>();
        q.offer(head);
        q1.offer(t1);  
        q2.offer(t2);
        while (!q1.isEmpty() && !q2.isEmpty()) {
            TreeNode node = q.poll();
            TreeNode node1 = q1.poll(); 
            TreeNode node2 = q2.poll();
            TreeNode left1 = node1.left;
            TreeNode left2 = node2.left;
            TreeNode right1 = node1.right;
            TreeNode right2 = node2.right;
            if(left1 != null || left2 != null){ 
                //两个左节点都存在
                if(left1 != null && left2 != null){ 
                    TreeNode left = new TreeNode(left1.val + left2.val);
                    node.left = left; 
                    //新节点入队列
                    q.offer(left);  
                    q1.offer(left1);
                    q2.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;
                    //新节点入队列
                    q.offer(right); 
                    q1.offer(right1);
                    q2.offer(right2);
                //只连接一个节点
                }else if(right1 != null)  
                    node.right = right1;
                 else 
                    node.right = right2;
            }
        }
        return head;
    }
}

C++实现代码:

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
        //若只有一个节点返回另一个,两个都为NULL自然返回NULL
        if (t1 == NULL)
            return t2;
        if (t2 == NULL)
            return t1;
        //合并根节点
        TreeNode* head = new TreeNode(t1->val + t2->val); 
        //连接后的树的层次遍历节点
        queue<TreeNode*> q; 
        //分别存两棵树的层次遍历节点
        queue<TreeNode*> q1; 
        queue<TreeNode*> q2;
        q.push(head);
        q1.push(t1);  
        q2.push(t2);
        while (!q1.empty() && !q2.empty()) {
            TreeNode *node = q.front(), *node1 = q1.front(), *node2 = q2.front();
            q.pop();
            q1.pop();
            q2.pop();
            TreeNode *left1 = node1->left, *left2 = node2->left, *right1 = node1->right, *right2 = node2->right;
            //两个左节点都存在
            if (left1 || left2) { 
                if (left1 && left2) {
                    TreeNode* left = new TreeNode(left1->val + left2->val);
                    node->left = left; 
                    //新节点入队列
                    q.push(left); 
                    q1.push(left1);
                    q2.push(left2);
                //只连接一个节点
                } else if (left1) 
                    node->left = left1;
                  else if (left2) 
                    node->left = left2;
            }
            if (right1 || right2) {
                //两个右节点都存在
                if (right1 && right2) { 
                    TreeNode* right = new TreeNode(right1->val + right2->val);
                    node->right = right;
                    //新节点入队列
                    q.push(right); 
                    q1.push(right1);
                    q2.push(right2);
                //只连接一个节点
                } else if (right1)  
                    node->right = right1;
                  else 
                    node->right = right2;
            }
        }
        return head;
    }
};

Python实现代码

import queue
class Solution:
    # 若只有一个节点返回另一个,两个都为None自然返回None
    def mergeTrees(self , t1: TreeNode, t2: TreeNode) -> TreeNode:
        if not t1:
            return t2
        if not t2:
            return t1
        # 合并根节点
        head = TreeNode(t1.val + t2.val) 
        # 连接后的树的层次遍历节点
        q = queue.Queue() 
        # 分别存两棵树的层次遍历节点
        q1 = queue.Queue() 
        q2 = queue.Queue()
        q.put(head)
        q1.put(t1)  
        q2.put(t2)
        while not q1.empty() and not q2.empty():
            node = q.get()
            node1 = q1.get()
            node2 = q2.get()
            left1 = node1.left
            left2 = node2.left
            right1 = node1.right
            right2 = node2.right
            # 两个左节点都存在
            if left1 or left2:  
                if left1 and left2:
                    left = TreeNode(left1.val + left2.val)
                    node.left = left
                    # 新节点入队列
                    q.put(left)  
                    q1.put(left1)
                    q2.put(left2)
                # 只连接一个节点
                elif left1: 
                    node.left = left1
                elif left2:
                    node.left = left2
            if right1 or right2:
                # 两个右节点都存在
                if right1 and right2: 
                    right = TreeNode(right1.val + right2.val)
                    node.right = right
                    # 新节点入队列
                    q.put(right)
                    q1.put(right1)
                    q2.put(right2)
                # 只连接一个节点
                elif right1:  
                    node.right = right1
                else:
                    node.right = right2
        return head

复杂度分析:

  • 时间复杂度: O ( m i n ( n , m ) ) O(min(n,m)) O(min(n,m)),m和n分别为两棵树的节点树,当一个树访问完时,自然就连接上另一个树的节点,故只访问了小树的节点数。
  • 空间复杂度: O ( m i n ( n , m ) ) O(min(n,m)) O(min(n,m)),辅助队列同时间,只访问了小树的节点数。
练习赛142是一场编程竞赛,通常包含多个算法题目,涵盖如数组、字符串、链表、动态规划等常见数据结构与算法知识点。针对这类比赛的解题思路和方法,可以从以下几个方面进行分析: ### 题目类型与解题策略 1. **数组相关问题** - 常见的题目包括查找数组中出现次数超过一半的数字、寻找缺失的数字、求解最大子数组和等。 - 解题方法包括使用哈希表统计频率、摩尔投票法(适用于多数元素问题)、双指针技巧或前缀和优化。 2. **链表操作** - 链表题目可能涉及反转链表、判断链表是否有环、找出两个链表的相交节点等。 - 例如,在找两个链表相交点的问题中,可以先计算各自长度,然后让长链表先走差值步数,再同步遍历比较节点地址[^3]。 3. **字符串处理** - 包括最长回文子串、无重复字符的最长子串等。 - 可采用滑动窗口、动态规划或中心扩展法等策略。 4. **树与图** - 树相关的题目可能涉及二叉树的遍历、路径和、最近公共祖先等问题。 - 图论问题可能需要使用深度优先搜索(DFS)、广度优先搜索(BFS)或拓扑排序等算法。 5. **动态规划** - 动态规划常用于解决背包问题、最长递增子序列、编辑距离等。 - 关键在于定义状态转移方程,并通过迭代或记忆化搜索进行求解。 6. **贪心算法** - 适用于区间调度、活动选择、硬币找零等问题。 - 贪心策略的核心在于每一步都做出局部最优选择。 ### 示例代码:摩尔投票法解决“多数元素”问题 ```python def majorityElement(nums): count = 0 candidate = None for num in nums: if count == 0: candidate = num count += (1 if num == candidate else -1) return candidate ``` 该算法时间复杂度为 O(n),空间复杂度为 O(1),非常适合处理大规模输入的数据集[^2]。 ### 提升解题能力的建议 - **刷题积累经验**:在 LeetCode、Codeforces、AtCoder 等平台上持续练习,熟悉各种题型。 - **学习经典算法**:掌握常见的算法模板,如二分查找、归并排序、快速选择等。 - **阅读官方题解与讨论区**:了解不同解法的优劣,尤其是最优解的时间复杂度分析。 - **模拟比赛训练**:定期参加在线编程比赛,提升实战能力和代码调试速度。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值