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

相关推荐
只是懒得想了7 小时前
C++实现密码破解工具:从MD5暴力破解到现代哈希安全实践
c++·算法·安全·哈希算法
m0_736919108 小时前
模板编译期图算法
开发语言·c++·算法
dyyx1118 小时前
基于C++的操作系统开发
开发语言·c++·算法
m0_736919108 小时前
C++安全编程指南
开发语言·c++·算法
蜡笔小马8 小时前
11.空间索引的艺术:Boost.Geometry R树实战解析
算法·r-tree
-Try hard-8 小时前
数据结构:链表常见的操作方法!!
数据结构·算法·链表·vim
2301_790300968 小时前
C++符号混淆技术
开发语言·c++·算法
我是咸鱼不闲呀8 小时前
力扣Hot100系列16(Java)——[堆]总结()
java·算法·leetcode
嵌入小生0078 小时前
单向链表的常用操作方法---嵌入式入门---Linux
linux·开发语言·数据结构·算法·链表·嵌入式
LabVIEW开发8 小时前
LabVIEW金属圆盘压缩特性仿真
算法·labview·labview知识·labview功能·labview程序