将以节点 r 为根的子树的 NAME 作为节点 r 的 NAME,记为 NAME(r),那么对于有根树 T1(V1,E1,r1) 和 T2(V2,E2,r2),如果 NAME(r1)=NAME(r2),那么 T1 和 T2 同构。
命名算法
实现
1234567891011Input. A rooted tree TOutput. The name of rooted tree TASSIGN-NAME(u)if u is a leafNAME(u) = (0)else for all child v of uASSIGN-NAME(v)sort the names of the children of uconcatenate the names of all children u to tempNAME(u) = (temp)
AHU 算法
实现
1234567810Input. Two rooted trees T1(V1,E1,r1) and T2(V2,E2,r2)Output. Whether these two trees are isomorphicAHU(T1(V1,E1,r1),T2(V2,E2,r2))ASSIGN-NAME(r1)ASSIGN-NAME(r2)if NAME(r1)=NAME(r2)return trueelsereturn false
复杂度证明
对于一颗有 n 个节点的有根树,假设他是链状的,那么节点名字长度最长可以是 n,那么 ASSIGN-NAME 算法的复杂度是 1+2+⋯+n 的常数倍,即 Θ(n2)。由此,朴素 AHU 算法的复杂度为 O(n2)。
优化的 AHU 算法
朴素的 AHU 算法的缺点是树的 NAME 的长度可能会过长,我们可以针对这一点做一些优化。
原理 1
对树进行层次划分,第 i 层的节点到根的最短距离为 i。位于第 i 层的节点的 NAME 可以 只 由位于第 i+1 层的节点的 NAME 拼接得到。
原理 2
在同一层内,节点的 NAME 可以由其在层内的排名唯一标识。
注意,这里的排名是对两棵树而言的,假设节点 u 位于第 i 层,那么节点 u 的排名等于所有 T1 和 T2 第 i 层的节点中 NAME 比 NAME(u) 小的节点的个数。
推论
我们可以将节点原来的 NAME 用其在层内的排名代替,然后把原来拼接节点 NAME 用向数组加入元素代替。
这样用整数和数组来代替字符串,既不会影响算法的正确性,又很大的降低了算法的复杂度。
复杂度证明
首先注意到第 i 层由拼接得到的 NAME 的总长度为第 i 层点的度数之和,即第 i+1 层的总点数,以下用 Li 表示。算法的下一步会将这些 NAME 看成字符串(数组)并排序,然后将它们替换为其在层内的排名(即重新映射为一个数)。以下引理表明了对总长为 L 的 m 个字符串排序的复杂度: