【码道初阶】【LeetCode 572】另一棵树的子树:当“递归”遇上“递归”

【LeetCode 572】另一棵树的子树:当"递归"遇上"递归"

在二叉树的问题中,有一类题目非常像"大海捞针"。题目给定两棵树 root(大树)和 subRoot(小树),问你:小树是不是大树的一部分?

这道题(LeetCode 572)其实是 LeetCode 100. 相同的树 的进阶版。如果你已经掌握了如何判断两棵树相同,那么解决这道题只需要在外面再套一层逻辑即可。

今天我们就来拆解这个"树中找树"的经典解法。

1. 核心解题思路:暴力匹配

要在 root 中找到 subRoot,逻辑其实非常直白:

拿着 subRoot 这张"模具",去 root 的每一个节点比对一下:

  1. 当前节点root 这棵树本身,和 subRoot 长得一样吗?
  2. 左子树 :如果不一样,那去 root左子树 里找找,有没有和 subRoot 一样的?
  3. 右子树 :如果还没找到,去 root右子树里找找?

只要以上三个地方有一个匹配成功,结果就是 true

这就引出了我们的两个核心函数:

  1. isSameTree:负责"判断两棵树是否完全相同"(即 LeetCode 100 的逻辑)。
  2. isSubtree:负责"遍历大树的每一个节点",并调用上面的函数。

2. 代码逻辑深度拆解

我们将代码分为两个部分来看,这样逻辑会非常清晰。

第一部分:判断"相同的树" (Helper Function)

这是判断的基石。给你两个根节点 pq,怎么判断它们代表的树是一模一样的?

java 复制代码
public boolean isSameTree(TreeNode p, TreeNode q) {
    // 1. 两个都为空,认为是相同的 -> True
    if(p == null && q == null) { return true; }
    
    // 2. 一个为空,一个不为空,肯定不同 -> False
    if(p == null && q != null) { return false; }
    if(p != null && q == null) { return false; }
    
    // 3. 都不为空,但值不一样 -> False
    if(p.val != q.val) { return false; }
    
    // 4. 当前节点值一样,必须左边相同 AND 右边也相同 -> 递归
    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
}
  • 同步递归 :这里的关键是 &&。两棵树要相同,必须是根节点值相同 ,且左子树相同 ,且右子树相同。缺一不可。

第二部分:在主树中"寻觅" (Main Logic)

有了上面的判断工具,主函数的任务就是遍历 root 的每一个节点,把它当作潜在的起点。

java 复制代码
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
    // 1. 边界处理:
    // 如果两棵树都空了,视为匹配成功
    if(root == null && subRoot == null) return true;
    // 如果大树空了(没路了),但小树还有,说明没找到 -> False
    if(root == null && subRoot != null) return false;
    // 如果大树还有,小树空了,题目通常定义空树是任何树的子树,
    // 但根据题目约束通常 subRoot 不为空,这里你的逻辑处理也没问题。
    if(root != null && subRoot == null) return false;

    // 2. 核心判断(三选一):
    
    // 情况 A:就在当前这个位置,两棵树完全一样!
    if (isSameTree(root, subRoot)) return true;
    
    // 情况 B:当前位置不一样,那我去左子树里找找看?
    if (isSubtree(root.left, subRoot)) return true;
    
    // 情况 C:左边也没找到,那我去右子树里找找看?
    if (isSubtree(root.right, subRoot)) return true;
    
    // 3. 找遍了左右都没找到 -> False
    return false;
}
  • 或运算 (||) 的逻辑 :这里用的是多次 if ... return true,本质上就是逻辑或。
    • 我是子树?
    • 或者,我的左孩子包含子树?
    • 或者,我的右孩子包含子树?
      只要满足其一,整棵树就包含子树。

3. 为什么代码中要写两个 if(p==null ...)?

isSameTreeisSubtree 中,都对 null 进行了详细的判断:

java 复制代码
if(p == null && q == null) return true;
if(p == null && q != null) return false;
if(p != null && q == null) return false;

这其实是递归的终止条件(Base Case)

  • 完全匹配 :必须两个指针同时走到 null,才说明之前的路径结构完全一致。
  • 结构不匹配:如果一个树走完了(null),另一个树还有节点(not null),说明结构不对等,直接判错。

4. 复杂度分析

假设 root 的节点数为 NNN,subRoot 的节点数为 MMM。

  • 时间复杂度 :O(N×M)O(N \times M)O(N×M)。
    • 在最坏的情况下(例如两棵树里的值全部相同),对于 root 的每一个节点,我们都要执行一次 isSameTree,而 isSameTree 最多遍历 MMM 个节点。
  • 空间复杂度 :O(max⁡(N,M))O(\max(N, M))O(max(N,M))。
    • 取决于递归调用栈的深度。

5. 总结

这道题完美展示了递归的嵌套使用

  1. 外层递归(isSubtree):负责遍历,类似于 DFS 寻找目标节点。
  2. 内层递归(isSameTree):负责比对,验证结构和值是否一致。

理解了这道题,你就掌握了二叉树问题的核心思想:将大问题拆解为当前节点的判断 + 左右子树的递归处理


相关推荐
咖啡星人k2 小时前
从Vibe Coding到Prompt Engineering:AI编程的新技能树
面试·职场和发展·ai编程
a1117762 小时前
LM 算法迭代过程动画演示(SLAM)
算法
头茬韭菜2 小时前
Context 的生死抉择:四层压缩、截断算法与 Session Memory
算法·ai
Jerry2 小时前
LeetCode 541. 反转字符串 II
算法
Jerry3 小时前
LeetCode 344. 反转字符串
算法
搞科研的小刘选手3 小时前
【香港大学主办&IEEE出版】第六届计算机视觉、应用与算法国际学术会议(CVAA 2026)
算法·计算机视觉·应用·学术会议
Java小白笔记3 小时前
Codex config.toml配置实战指南
人工智能·算法·chatgpt·ai编程·集成学习
王老师青少年编程3 小时前
2026年6月GESP真题及题解(C++七级):消消乐
数据结构·c++·算法·真题·gesp·2026年6月
z小猫不吃鱼3 小时前
模型剪枝经典论文精读:Pruning Filters for Efficient ConvNets
算法·机器学习·剪枝
Fox爱分享5 小时前
字节二面:1000瓶酒,有一瓶是毒药,多少只老鼠可以查出来?
算法·面试·程序员