力扣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. 尝试实现序列化方法的优化版本

相关推荐
BothSavage7 小时前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn7 小时前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法
烬羽8 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
先吃饱再说1 天前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰1 天前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术1 天前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六1 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术1 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize1 天前
初识DFS 与 BFS:递归、队列与图遍历
算法