在问 · 算法题解

572. 另一棵树的子树

简单二叉树递归逐点匹配力扣原题 ↗

题目描述

输入两棵二叉树的根节点 root 和 subRoot,均用层序数组表示,null 表示空位。

以 root 中某个节点为根、连同它的全部后代组成的树,称为 root 的一棵子树;root 整棵树也算它自己的子树。

判断 root 中是否存在一棵子树,其形状与 subRoot 完全一致,并且对应位置的节点值都相等。存在返回 true,否则返回 false。

示例 1
输入:root = [3,4,5,1,2], subRoot = [4,1,2]
输出:true

解释:以值为 4 的节点为根的子树正好是 [4,1,2]。

示例 2
输入:root = [3,4,5,1,2,null,null,null,null,0], subRoot = [4,1,2]
输出:false

解释:值为 4 的节点下面还挂着节点 0,整棵子树与 subRoot 不一致。

数据范围

  • root 的节点数在 [1, 2000] 范围内。
  • subRoot 的节点数在 [1, 1000] 范围内。
  • -10^4 <= root.val <= 10^4
  • -10^4 <= subRoot.val <= 10^4

解题思路:递归逐点匹配

先写一个 same(a, b) 判断两棵树是否完全相同:都为空才相同,一空一非空不同,否则比较根值再递归比较左右。然后对 root 的每个节点做候选根:当前节点与 subRoot 相同就找到了,否则到左、右子树里继续找。or 的短路保证找到后立刻停止。

为什么正确

isSubtree(node) 为真,当且仅当 node 本身与 subRoot 相同,或者左、右子树中某一侧包含 subRoot。

复杂度分析

时间 O(n·m),n、m 为两棵树的节点数(最坏每个候选根都比较 m 次);空间 O(h),为递归深度。

易错点

子树必须包含该节点的全部后代。示例 2 中 4 下面的 2 还挂着 0,所以 [4,1,2] 不是它的子树。只比较“开头一样”或只比较值序列都会出错。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
        def same(a, b):
            if not a or not b:
                return a is b
            return a.val == b.val and same(a.left, b.left) and same(a.right, b.right)
        if not root:
            return False
        return same(root, subRoot) or self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)

载入编辑器,自己跑一遍 →

其他解法

递归逐点匹配代码短、不占额外数组,但最坏 O(m·n);序列化 + KMP 把树匹配转成串匹配,最坏也是线性。

解法 2:先序序列 + KMP 时间 O(m + n),空间 O(m + n)

用先序遍历把两棵树各自序列化成数组,空子树也写入一个特殊标记。加入空标记后,序列唯一确定一棵树,且一棵子树的序列是原树序列中的一段连续片段。于是问题变成在 root 的序列中查找 subRoot 的序列,用 KMP 的前缀函数做线性匹配。m、n 为两棵树的节点数。

最坏情况下是线性时间,不会退化成 O(m·n)。代价是要额外存两个序列,并且必须写入空标记才能保证匹配正确。

class Solution:
    def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
        NIL = 1 << 30

        def serialize(node, out):
            if not node:
                out.append(NIL)
                return
            out.append(node.val)
            serialize(node.left, out)
            serialize(node.right, out)

        text, pat = [], []
        serialize(root, text)
        serialize(subRoot, pat)
        fail = [0] * len(pat)
        k = 0
        for i in range(1, len(pat)):
            while k > 0 and pat[i] != pat[k]:
                k = fail[k - 1]
            if pat[i] == pat[k]:
                k += 1
            fail[i] = k
        k = 0
        for x in text:
            while k > 0 and x != pat[k]:
                k = fail[k - 1]
            if x == pat[k]:
                k += 1
            if k == len(pat):
                return True
        return False

二叉树 专题的其他题目

查看 二叉树 全部题目 →