【码道初阶】【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):负责比对,验证结构和值是否一致。

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


相关推荐
月明长歌2 小时前
【码道初阶】【LeetCode 150】逆波兰表达式求值:为什么栈是它的最佳拍档?
java·数据结构·算法·leetcode·后缀表达式
C雨后彩虹2 小时前
最大数字问题
java·数据结构·算法·华为·面试
java修仙传2 小时前
力扣hot100:搜索二维矩阵
算法·leetcode·矩阵
浅川.253 小时前
xtuoj 字符串计数
算法
天`南3 小时前
【群智能算法改进】一种改进的金豺优化算法IGJO[1](动态折射反向学习、黄金正弦策略、自适应能量因子)【Matlab代码#94】
学习·算法·matlab
Han.miracle3 小时前
数据结构与算法--006 和为s的两个数字(easy)
java·数据结构·算法·和为s的两个数字
AuroraWanderll3 小时前
C++类和对象--访问限定符与封装-类的实例化与对象模型-this指针(二)
c语言·开发语言·数据结构·c++·算法
月明长歌3 小时前
【码道初阶】LeetCode 622:设计循环队列:警惕 Rear() 方法中的“幽灵数据”陷阱
java·算法·leetcode·职场和发展
mit6.8243 小时前
博弈-翻转|hash<string>|smid
算法