1.1. 什么是树的同构?

树的同构(Tree Isomorphism)是图同构的一种特殊情况。因为树是无环连通图,所以判断两个树是否同构,本质上是在判断它们是否具有相同的结构。

举个直观的例子:两个树虽然节点名称不同,但它们的父子关系、层级结构、分支形态完全一致。此时我们称这两个树是同构的

要证明两个树是否同构,可以通过重新绘制其中一个树的结构,使其与另一个完全一致。但这手动操作并不总是容易的,因此我们需要一个高效的算法形式化定义

✅ 同构的数学定义

设两个树为:

  • $ T_1 = (V_1, E_1) $
  • $ T_2 = (V_2, E_2) $

其中:

  • $ V_1, V_2 $:树的节点集合
  • $ E_1, E_2 $:树的边集合

若存在一个双射函数 $ f: V_1 \rightarrow V_2 $,使得任意两个节点 $ u, v \in V_1 $ 在 $ T_1 $ 中相邻 当且仅当 它们在 $ T_2 $ 中也相邻,即:

$$ (u, v) \in E_1 \iff (f(u), f(v)) \in E_2 $$

则称 $ T_1 $ 和 $ T_2 $ 是同构的,函数 $ f $ 称为它们的同构映射


1.2. 树结构的编码(Encoding)

为了判断两个树是否同构,我们可以对每个树进行编码(Encoding),使得只有结构相同的树才会有相同的编码。

✅ AHU 编码(Aho-Hopcroft-Ullman)

AHU 编码是一种基于子树结构的递归编码方法。其核心思想是:

  • 如果节点是叶子节点,则编码为 0
  • 如果节点是非叶子节点,则将其子节点的编码按升序排列,然后用括号包裹起来作为该节点的编码。

举个例子:

    A
   /|\
  B C D
     |
     E

该树的 AHU 编码如下:

  • 叶子节点 B、C、E 的编码是 0
  • D 的子节点只有一个 E,所以编码是 (0)
  • A 的子节点是 B、C、D,它们的编码分别是 00(0),排序后是 0, 0, (0),所以 A 的编码是 (0)(0)(0)

最终,整棵树的编码为 (0)(0)(0)


1.3. 算法实现(伪代码)

algorithm AHUEncoding(T):
    // 输入:一棵树 T
    // 输出:该树的 AHU 编码

    D <- 构建字典,记录每一层的节点(根节点为第 1 层)

    for u in D[h]:  // h 是树的高度
        AHU(u) <- '0'

    k <- h - 1

    while k >= 1:
        for u in D[k]:
            if u 是叶子节点:
                AHU(u) <- '0'
            else:
                AHU(u) <- '(' + 将 u 的子节点的 AHU 编码按升序排列 + ')'
        k <- k - 1

    return AHU(T.root)

1.4. 同构检测算法

⚠️ 问题:根节点不同怎么办?

两个同构的树如果根节点不同,它们的 AHU 编码可能不同。例如:

    A        B
   / \      / \
  B   C    A   C

这两个树结构相同,但根节点不同,AHU 编码会不同。

✅ 解决方案:使用树的“中心”作为根

树的中心(Center)是指树中最短路径的中点节点。树的中心最多有两个。

我们对两个树分别找出它们的中心,并尝试以这些中心为根进行 AHU 编码比较。

algorithm CheckIsomorphicTrees(T1, T2):
    // 输入:两个树 T1 和 T2
    // 输出:是否同构

    for center1 in FIND-CENTERS(T1):
        T1.root <- center1

        for center2 in FIND-CENTERS(T2):
            T2.root <- center2

            if AHU(T1) == AHU(T2):
                return true

    return false

1.5. 优化版本(按层级比较)

我们可以进一步优化这个算法,通过按层级比较 AHU 编码,一旦发现某一层不匹配,就提前终止比较。

algorithm FasterIsomorphismCheck(T1, T2):
    for center1 in FIND-CENTERS(T1):
        T1.root <- center1

        for center2 in FIND-CENTERS(T2):
            T2.root <- center2

            D1 <- 构建 T1 的层级节点字典
            D2 <- 构建 T2 的层级节点字典

            if T1 的高度 != T2 的高度:
                return false

            h <- T1 的高度

            for u in D1[h] + D2[h]:
                AHU(u) <- 0

            k <- h - 1

            while k >= 1:
                L1 <- 对 T1 第 k 层节点的 AHU 编码排序
                L2 <- 对 T2 第 k 层节点的 AHU 编码排序

                if L1 != L2:
                    return false

                k <- k - 1

            return true

    return false

1.6. 时间复杂度分析

  • 每个节点仅被处理一次,时间复杂度为 $ O(n) $
  • 每次排序操作的节点数量固定,不随 $ n $ 增长,因此排序的总时间复杂度为 $ O(h) $,其中 $ h $ 是树的高度
  • 最坏情况下,两个树各有两个中心,需要比较最多 4 次

最终时间复杂度为 $ O(n) $


1.7. 小结

  • 树的同构意味着它们具有相同的结构
  • 可以使用 AHU 编码来表示树的结构
  • 通过以树的中心为根进行编码比较,可以高效判断两个树是否同构
  • 优化算法可以在层级上提前终止比较,提升效率
  • 整体时间复杂度为线性 $ O(n) $,适合大规模树结构比较

📌 常见术语对照表

英文术语 中文术语 备注
isomorphism 同构
tree
node 节点
leaf node 叶子节点
encoding 编码
AHU encoding AHU 编码 Aho-Hopcroft-Ullman 编码算法
center 中心 树中最短路径的中点节点

📚 扩展阅读


原始标题:Isomorphic Trees