力扣572. 另一棵树的子树-递归

问题描述

给定两棵二叉树 rootsubRoot,检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true;否则返回 false

二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。

复制代码
输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true
复制代码
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

解题思路

核心思想

解决这个问题的关键在于两点:

  1. 如何判断两棵树完全相同

  2. 如何在一棵树中寻找与目标树相同的子树

我们采用递归的思想来解决:

  • 首先实现一个辅助函数来判断两棵树是否完全相同

  • 然后在主函数中,检查当前树是否与目标树相同,如果不同则递归检查左右子树

算法步骤

  1. 处理边界情况

    • 如果 subRoot 为空树,根据定义空树是任何树的子树,返回 true

    • 如果 root 为空而 subRoot 不为空,返回 false

  2. 判断当前树是否相同

    • 使用辅助函数 isSame 判断以当前节点为根的树是否与 subRoot 完全相同

    • 如果相同,直接返回 true

  3. 递归搜索左右子树

    • 如果不相同,则继续在左子树和右子树中寻找

    • 只要任意一边找到就返回 true

代码实现

java 复制代码
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        // 处理边界情况
        if (subRoot == null) {
            return true; // 空树是任何树的子树
        }
        if (root == null) {
            return false; // 非空子树不可能是空树的子树
        }
        
        if(isSame(root,subRoot)){
            return true;
        }
        return isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
    }
    
    // 辅助函数:判断两棵树是否完全相同
    public boolean isSame(TreeNode root1, TreeNode root2) {
        // 两个都为空
        if (root1 == null && root2 == null) {
            return true;
        }
        // 一个为空,一个不为空
        if (root1 == null || root2 == null) {
            return false;
        }
        // 值不相等
        if (root1.val != root2.val) {
            return false;
        }
        // 递归比较左右子树
        return isSame(root1.left, root2.left) && 
               isSame(root1.right, root2.right);
    }
}

复杂度分析

时间复杂度

  • 最坏情况 :O(m × n),其中 m 是 root 的节点数,n 是 subRoot 的节点数

  • 最佳情况 :O(min(m, n)),当在 root 的根节点就找到匹配时

  • 我们需要遍历 root 中的每个节点(m 个),对于每个节点,可能需要与 subRoot 的所有节点进行比较(n 个)

空间复杂度

  • 递归栈深度 :O(h),其中 h 是 root 的高度

  • 在最坏情况下(树退化为链表),空间复杂度为 O(m)

  • 在平衡树情况下,空间复杂度为 O(log m)

关键点解析

  1. 递归思想的应用

    • 通过递归分解问题,将大问题转化为小问题

    • isSame 函数递归比较两棵树的每个节点

    • isSubtree 函数递归搜索可能的子树

  2. 边界条件的处理

    • 空树的处理是关键,空树是任何树的子树

    • 需要考虑各种可能的 null 情况组合

  3. 短路优化

    • 使用 || 运算符,一旦找到匹配就立即返回,避免不必要的搜索

    • isSame 中,一旦发现值不相等或结构不同就立即返回

常见错误

  1. 忽略空树情况

    java 复制代码
    // 错误示例:没有正确处理 subRoot 为空的情况
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if (root == null) return false;
        // 缺少对 subRoot == null 的处理
    }
  2. 逻辑判断不完整

    java 复制代码
    // 错误示例:isSame 函数中处理一个为空的情况不完整
    public boolean isSame(TreeNode root1, TreeNode root2) {
        if (root1 == null) return root2 == null;
        if (root2 == null) return root1 == null; // 此时 root1 不为 null,应该返回 false
    }

优化思路

虽然上述解法的时间复杂度为 O(m × n),但实际中通常表现良好。如果需要进一步优化,可以考虑以下方法:

  1. 序列化方法

    • 将两棵树序列化为字符串

    • 判断 subRoot 的序列化字符串是否是 root 序列化字符串的子串

    • 时间复杂度:O(m + n)

  2. 哈希方法

    • 为每棵子树计算哈希值

    • 比较哈希值来判断子树是否相同

    • 时间复杂度:O(m + n)

总结

判断一棵树是否是另一棵树的子树是二叉树中的经典问题,主要考察递归思想和对树结构的理解。通过分解问题为"判断两棵树相同"和"在树中搜索",我们可以清晰地解决这个问题。

掌握这种递归分解问题的思想,对于解决其他树相关问题(如对称树、平衡树等)也大有帮助。


练习建议

  1. 尝试使用迭代方法实现相同的功能

  2. 思考如何处理重复值的情况

  3. 尝试实现序列化方法的优化版本

相关推荐
漂流瓶jz13 小时前
UVA-11214 守卫棋盘 题解答案代码 算法竞赛入门经典第二版
c++·算法·dfs·aoapc·算法竞赛入门经典·迭代加深搜索·八皇后
浮生091913 小时前
DHUOJ 基础 88 89 90
算法
v_for_van14 小时前
力扣刷题记录7(无算法背景,纯C语言)
c语言·算法·leetcode
先做个垃圾出来………14 小时前
3640. 三段式数组 II
数据结构·算法
tankeven15 小时前
HJ93 数组分组
c++·算法
Σίσυφος190015 小时前
LM 在 PnP(EPnP / P3P)的应用
算法
陈天伟教授15 小时前
人工智能应用- 人工智能交叉:01. 破解蛋白质结构之谜
人工智能·神经网络·算法·机器学习·推荐算法
LightYoungLee16 小时前
General-behavior interview tutorials
算法
I_LPL16 小时前
day34 代码随想录算法训练营 动态规划专题2
java·算法·动态规划·hot100·求职面试
We་ct16 小时前
LeetCode 105. 从前序与中序遍历序列构造二叉树:题解与思路解析
前端·算法·leetcode·链表·typescript