Description

給定兩棵二元樹 root 和 subRoot,判斷 root 中是否存在一棵子樹,與 subRoot 結構和值完全相同。

Example:

Input: root = [3,4,5,1,2], subRoot = [4,1,2] Output: true

Intuition#

對 root 的每個節點檢查:以它為根的子樹是否和 subRoot 相同。

  • 結合「遍歷每個節點」和「判斷兩棵樹是否相同」(Same Tree)
  • 也可以序列化兩棵樹再用字串匹配

Approaches#

⭐ 1: Recursive DFS + Same Tree#

  • 概念: 對 root 的每個節點呼叫 isSameTree,若任一匹配成功則回傳 true
  • 時間複雜度: O(m * n),m 和 n 分別為兩棵樹的節點數
  • 空間複雜度: O(h),h 為 root 的高度
class Solution {
    fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean {
        if (root == null) return subRoot == null
        return isSameTree(root, subRoot) ||
               isSubtree(root.left, subRoot) ||
               isSubtree(root.right, subRoot)
    }

    private fun isSameTree(p: TreeNode?, q: TreeNode?): Boolean {
        if (p == null && q == null) return true
        if (p == null || q == null) return false
        return p.`val` == q.`val` &&
               isSameTree(p.left, q.left) &&
               isSameTree(p.right, q.right)
    }
}

2: 序列化 + 字串匹配#

  • 概念: 將兩棵樹序列化為字串,用字串匹配判斷 subRoot 的序列化是否為 root 序列化的子字串
  • 時間複雜度: O(m + n)(使用 KMP 或內建字串搜尋)
  • 空間複雜度: O(m + n)
class Solution {
    fun isSubtree(root: TreeNode?, subRoot: TreeNode?): Boolean {
        val rootStr = serialize(root)
        val subStr = serialize(subRoot)
        return rootStr.contains(subStr)
    }

    private fun serialize(node: TreeNode?): String {
        if (node == null) return ",#"
        return ",${node.`val`}${serialize(node.left)}${serialize(node.right)}"
    }
}
注意序列化的陷阱

序列化時需要加分隔符(如逗號)避免數值混淆。例如 121,2 不同。 使用前綴逗號 ,val 的格式可以確保每個節點值被明確界定。

🔑 Takeaways#

  • Pattern: 子樹問題 = 遍歷 + Same Tree 檢查
  • 關鍵技巧: 善用已解決的子問題(Same Tree)來組合更複雜的問題