AHU Algorithm
The AHU algorithm is used to determine whether two rooted trees are isomorphic.
Another common approach for determining tree isomorphism is Tree Hashing.
Prerequisites: Tree Basics, Tree Centroids
It is recommended to read along with the examples given in the references.
Definition of Tree Isomorphism
Rooted Tree Isomorphism
For two rooted trees \(T_1(V_1,E_1,r_1)\) and \(T_2(V_2,E_2,r_2)\), if there exists a bijection \(\varphi: V_1 \rightarrow V_2\) such that
and \(\varphi(r_1)=r_2\) holds, then the rooted trees \(T_1(V_1,E_1,r_1)\) and \(T_2(V_2,E_2,r_2)\) are said to be isomorphic.
Unrooted Tree Isomorphism
For two unrooted trees \(T_1(V_1,E_1)\) and \(T_2(V_2,E_2)\), if there exists a bijection \(\varphi: V_1 \rightarrow V_2\) such that
holds, then the unrooted trees \(T_1(V_1,E_1)\) and \(T_2(V_2,E_2)\) are said to be isomorphic.
In simple terms, if by relabeling all nodes of tree \(T_1\), tree \(T_1\) becomes exactly the same as tree \(T_2\), then the two trees are isomorphic.
Transformation of the Problem
The unrooted tree isomorphism problem can be transformed into the rooted tree isomorphism problem. The specific method is as follows:
For unrooted trees \(T_1(V_1, E_1)\) and \(T_2(V_2,E_2)\), first find all their centroids.
- If the numbers of centroids of these two unrooted trees are different, then the two trees are not isomorphic.
- If the numbers of centroids of these two unrooted trees are both \(1\), denoted as \(c_1\) and \(c_2\) respectively, then if the rooted tree \(T_1(V_1,E_1,c_1)\) and the rooted tree \(T_2(V_2,E_2,c_2)\) are isomorphic, then the unrooted trees \(T_1(V_1, E_1)\) and \(T_2(V_2,E_2)\) are isomorphic, otherwise they are not.
- If the numbers of centroids of these two unrooted trees are both \(2\), denoted as \(c_1,c'_1\) and \(c_2,c'_2\) respectively, then if the rooted tree \(T_1(V_1,E_1,c_1)\) and the rooted tree \(T_2(V_2,E_2,c_2)\) are isomorphic or the rooted tree \(T_1(V_1,E_1,c'_1)\) and \(T_2(V_2,E_2,c_2)\) are isomorphic, then the unrooted trees \(T_1(V_1, E_1)\) and \(T_2(V_2,E_2)\) are isomorphic, otherwise they are not.
Therefore, as long as the rooted tree isomorphism problem is solved, we can transform the unrooted tree isomorphism problem into a rooted tree isomorphism problem according to the above method, and then solve the unrooted tree isomorphism problem.
Assuming there is an algorithm that can solve the rooted tree isomorphism problem in \(O(\left|V\right|)\) time, then according to the above method we can also solve the unrooted tree isomorphism problem in \(O(\left|V\right|)\) time.
Naive AHU Algorithm
The naive AHU algorithm is based on bracket sequences.
Principle 1
We know that a valid bracket sequence uniquely corresponds to a rooted tree, and the bracket sequence of a tree is formed by concatenating the bracket sequences of its subtrees. If we obtain a new bracket sequence by changing the order of concatenating the subtree bracket sequences, then the tree corresponding to the new bracket sequence is isomorphic to the tree corresponding to the original bracket sequence.
Principle 2
The tree isomorphism relation is transitive. That is, if \(T_1\) is isomorphic to \(T_2\), and \(T_2\) is isomorphic to \(T_3\), then \(T_1\) is isomorphic to \(T_3\).
Corollary
Consider the recursive algorithm for computing the tree bracket sequence. When backtracking, we concatenate the bracket sequences of the subtrees. If during concatenation, we concatenate the sequences in lexicographically increasing order and denote the final result as \(NAME\).
Let the \(NAME\) of the subtree rooted at node \(r\) be the \(NAME\) of node \(r\), denoted as \(NAME(r)\). Then for rooted trees \(T_1(V_1,E_1,r_1)\) and \(T_2(V_2,E_2,r_2)\), if \(NAME(r_1)=NAME(r_2)\), then \(T_1\) and \(T_2\) are isomorphic.
Naming Algorithm
Implementation
AHU Algorithm
Implementation
Complexity Proof
For a rooted tree with \(n\) nodes, assuming it is chain-like, the maximum length of a node's name can be \(n\). Then the complexity of the ASSIGN-NAME algorithm is a constant multiple of \(1+2+\cdots+n\), i.e., \(\Theta(n^2)\). Consequently, the complexity of the naive AHU algorithm is \(O(n^2)\).
Optimized AHU Algorithm
The drawback of the naive AHU algorithm is that the \(NAME\) of a tree can be excessively long. We can make some optimizations regarding this point.
Principle 1
Partition the tree into levels, where the shortest distance from a node at level \(i\) to the root is \(i\). The \(NAME\) of a node at level \(i\) can be formed solely by concatenating the \(NAME\)s of nodes at level \(i+1\).
Principle 2
Within the same level, the \(NAME\) of a node can be uniquely identified by its rank within the level.
Note: Here, the ranking is relative to both trees. Suppose node \(u\) is at level \(i\). Then the rank of node \(u\) equals the number of nodes in level \(i\) of both \(T_1\) and \(T_2\) whose \(NAME\) is lexicographically smaller than \(NAME(u)\).
Corollary
We can replace the original \(NAME\) of a node with its rank within the level, and then replace the concatenation of node \(NAME\)s with appending elements to an array.
Using integers and arrays instead of strings does not affect the correctness of the algorithm and greatly reduces its complexity.
Complexity Proof
First, note that the total length of the \(NAME\)s obtained by concatenation at level \(i\) equals the sum of the degrees of nodes at level \(i\), which is the total number of nodes at level \(i+1\), denoted by \(L_i\) below. The next step of the algorithm treats these \(NAME\)s as strings (arrays), sorts them, and then replaces them with their rank within the level (i.e., remaps them to a number). The following lemma indicates the complexity of sorting \(m\) strings with total length \(L\):
- We can use radix sort to complete the sorting in \(O(L+|\Sigma|)\) time, where \(|\Sigma|\) is the size of the character set. (There are some implementation details; see the references.)
- We can use quicksort to complete the sorting in \(O(L \log m)\) time. The general idea of the proof is that the height of the quicksort recursion tree is \(O(\log m)\), and the complexity of naively comparing two strings of lengths \(\ell_1\) and \(\ell_2\) is \(O(\min\{\ell_1,\ell_2\})\).
In the AHU algorithm, the character set size for strings at level \(i\) is at most the number of nodes at level \(i+1\), i.e., \(L_i\), so radix sort runs in linear time. Since \(\sum_i L_i=O(n)\), summing the complexity of each level shows that if radix sort for strings is used, the total complexity of the algorithm is \(T(n)=O(n)\). Similarly, if quicksort is used to sort the strings, then \(T(n)=O(n \log n)\).
Example Problem
Problem translation: Given two unrooted trees, determine whether they are isomorphic.
Reference Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | |
References
Most of this article is translated from the Paper and Slide. The proofs in the references are more comprehensive and rigorous; this article makes certain simplifications.
For the complexity analysis of the AHU algorithm and the linear-time radix sort algorithm for strings, refer to Section 3.2 Radix sorting and Example 3.2 in The Design and Analysis of Computer Algorithms.
本页面最近更新:,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:OI-wiki
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用