Ja, Sie können diese Komprimierung in , aber es ist nicht einfach :) Wir machen zuerst einige Beobachtungen und stellen dann den Algorithmus vor. Wir gehen davon aus, dass der Baum zunächst nicht komprimiert ist - dies ist nicht wirklich erforderlich, erleichtert aber die Analyse.O(nlogn)
Erstens charakterisieren wir "strukturelle Gleichheit" induktiv. Sei und T ' zwei (Unter-) Bäume. Wenn T und T ' beide Nullbäume sind (überhaupt keine Eckpunkte haben), sind sie strukturell äquivalent. Wenn T und T ' beide keine Nullbäume sind, sind sie strukturell äquivalent, wenn ihre linken Kinder strukturell äquivalent sind und ihre rechten Kinder strukturell äquivalent sind. "Strukturelle Äquivalenz" ist der minimale Fixpunkt über diese Definitionen.TT′TT′TT′
Beispielsweise sind zwei beliebige Blattknoten strukturell äquivalent, da beide die Nullbäume als ihre beiden untergeordneten Objekte haben, die strukturell äquivalent sind.
Da es ziemlich ärgerlich ist zu sagen, dass "ihre linken Kinder strukturell gleichwertig sind und ihre rechten Kinder", werden wir oft sagen, dass "ihre Kinder strukturell gleichwertig sind" und dasselbe beabsichtigen. Beachten Sie auch, dass wir manchmal "diesen Scheitelpunkt" sagen, wenn wir "den an diesem Scheitelpunkt verwurzelten Teilbaum" meinen.
Die obige Definition gibt uns sofort einen Hinweis, wie die Komprimierung durchzuführen ist: Wenn wir die strukturelle Äquivalenz aller Teilbäume mit einer Tiefe von höchstens , können wir die strukturelle Äquivalenz der Teilbäume mit einer Tiefe von d + 1 leicht berechnen . Wir müssen diese Berechnung auf intelligente Weise durchführen, um eine Laufzeit von O ( n 2 ) zu vermeiden .dd+1O(n2)
Der Algorithmus weist jedem Scheitelpunkt während seiner Ausführung Bezeichner zu. Ein Bezeichner ist eine Zahl in der Menge . Bezeichner sind eindeutig und ändern sich nie: Wir gehen daher davon aus, dass wir zu Beginn des Algorithmus eine (globale) Variable auf 1 setzen. Jedes Mal, wenn wir einem Scheitelpunkt einen Bezeichner zuweisen, weisen wir dem Scheitelpunkt und dem Inkrement den aktuellen Wert dieser Variablen zu der Wert dieser Variablen.{1,2,3,…,n}
Wir transformieren zuerst den Eingabebaum in (höchstens ) Listen, die Scheitelpunkte gleicher Tiefe enthalten, zusammen mit einem Zeiger auf deren Eltern. Dies ist leicht in O ( n ) Zeit möglich.nO(n)
Wir komprimieren zuerst alle Blätter (wir können diese Blätter in der Liste mit Scheitelpunkten der Tiefe 0 finden) zu einem einzigen Scheitelpunkt. Wir weisen diesem Vertex einen Bezeichner zu. Die Komprimierung von zwei Scheitelpunkten erfolgt durch Umleiten des übergeordneten Scheitelpunkts auf den anderen Scheitelpunkt.
Wir machen zwei Beobachtungen: Erstens hat jeder Scheitelpunkt Kinder mit einer streng kleineren Tiefe, und zweitens, wenn wir alle Scheitelpunkte mit einer Tiefe kleiner als komprimiert haben (und ihnen Bezeichner gegeben haben), dann sind zwei Scheitelpunkte mit der Tiefe d strukturell gleich und kann komprimiert werden, wenn die Bezeichner ihrer Kinder übereinstimmen. Diese letzte Beobachtung ergibt sich aus dem folgenden Argument: Zwei Eckpunkte sind strukturell äquivalent, wenn ihre Kinder strukturell äquivalent sind, und nach der Komprimierung bedeuten dies, dass ihre Zeiger auf dieselben Kinder zeigen, was wiederum bedeutet, dass die Bezeichner ihrer Kinder gleich sind.dd
Wir durchlaufen alle Listen mit Knoten gleicher Tiefe von kleiner bis großer Tiefe. Für jede Ebene erstellen wir eine Liste von ganzzahligen Paaren, wobei jedes Paar den Bezeichnern der untergeordneten Elemente eines Scheitelpunkts auf dieser Ebene entspricht. Wir haben, dass zwei Eckpunkte in dieser Ebene strukturell äquivalent sind, wenn ihre entsprechenden ganzzahligen Paare gleich sind. Unter Verwendung der lexikografischen Reihenfolge können wir diese sortieren und die Mengen von ganzzahligen Paaren erhalten, die gleich sind. Wir komprimieren diese Mengen wie oben beschrieben zu einzelnen Eckpunkten und geben ihnen Bezeichner.
Die obigen Beobachtungen beweisen, dass dieser Ansatz funktioniert und zum komprimierten Baum führt. Die Gesamtlaufzeit beträgt plus der Zeit, die zum Sortieren der von uns erstellten Listen benötigt wird. Da die Gesamtzahl der von uns erstellten Ganzzahlpaare n ist , ergibt sich , wie erforderlich , eine Gesamtlaufzeit von O ( n log n ) . Es ist trivial, zu zählen, wie viele Knoten am Ende des Vorgangs noch übrig sind (sehen Sie sich nur an, wie viele Identifikatoren wir ausgegeben haben).O(n)nO(nlogn)