572. 另一棵树的子树(简单)

Blind 75 › 572. 另一棵树的子树(简单) (第 4 章)
文章工具

题目

给定两棵二叉树 root 与 subRoot,判断 root 中是否存在某个节点,使它以及它的全部后代所构成的树,与 subRoot 在结构和节点值上完全相同。存在返回 true,否则返回 false。

注意「子树」不是「部分重叠」:候选节点以下必须整棵一致,连空孩子的数量、位置都不能差。任意一棵树也可以看作它自身的子树。

自造示例一:

root:        1
          /     
         2       3
        /      / 
       4   5   6   7

subRoot:      2
            / 
           4   5

输出:true

自造示例二:

root:        1
          /     
         2       3
        / 
       4   5

subRoot:      2
            /
           4

输出:false

例二中 root 里节点 2 有两个孩子 4,5,而 subRoot 的节点 2 只有一个左孩子 4;即使值能对上,结构不同也不能算。

约束:root 节点数 1 ≤ n ≤ 2000;subRoot 节点数 1 ≤ m ≤ 1000;节点值范围 -104 ≤ val ≤ 104。

思路

一句话:外层递归负责「在 root 里找候选根」,内层递归负责「判断从这个候选根开始是否与 subRoot 完全同构」。

最直白的想法是:既然子树可以从 root 的任意节点开始,就把每个节点都当作候选根,逐个拿去和 subRoot 比较。这个方向是对的;但只比较「当前节点值相等」是不够的,因为值相等不代表整棵子树相同,还必须一路比到左右孩子、比到叶子,空节点也要一一对应。

关键观察:二叉树是否相等,本身可以用递归精确描述——两棵树相等,当且仅当根值相等,且左子树相等、右子树相等。于是可以把问题拆成两个函数:一个负责「找起点」,一个负责「判相同」。

为什么这就够用?root 最多 2000 个候选起点,每次比较最多访问 subRoot 的 1000 个节点,最坏约 2 × 106 次访问,规模很小;而且一旦发现值不同或空位不同就立即返回,实际远快于上界,不需要更复杂的序列化技巧。

解法

递归流程如下:

  1. 若 subRoot 为空,空树可视为任意树的子树,直接返回 true(本题数据非空,这里是稳健处理)。
  2. 若 root 为空而 subRoot 非空,说明这条路已经走到底仍没匹配,返回 false。
  3. 先拿当前 root 当候选根,调用 same(root, subRoot);命中就返回 true。
  4. 未命中则分别到左、右子树继续找,两边是短路或:左边命中 || 右边命中。
  5. same(p, q):两空为真;一空一非空为假;值不等为假;否则递归比较左右子树。
当前候选根same 比较动作
节点 11 ≠ 2,立即失败继续去左子树
节点 22=2,4=4,5=5,左右递归到底命中,返回 true

🔒

本篇为付费内容

您已阅读试读部分,购买后即可解锁全文。

⚠️ 本站内容已做唯一性标记并受版权保护,未经授权禁止转载,侵权必究。