„AVL-Baum“ – Versionsunterschied

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen
[gesichtete Version][gesichtete Version]
Inhalt gelöscht Inhalt hinzugefügt
K versch Kleinigkeiten
Anwendungen, Historisches ausgebaut
Zeile 199: Zeile 199:
Wenn bei einer Operation die AVL-Balance verloren geht, das heißt ein Höhenunterschied von mehr als 1 zwischen zwei Geschwister-Teilbäumen entsteht, muss eine Rebalancierung durchgeführt werden. Dies muss innerhalb der aufgerufenen Funktion „automatisch“ (daher die Charakterisierung als ''self-balancing tree'' im Englischen) geschehen. Geeignete Reparaturwerkzeuge sind die sogenannten [[Binärbaum#Rotation in binären Bäumen|Rotationen]].
Wenn bei einer Operation die AVL-Balance verloren geht, das heißt ein Höhenunterschied von mehr als 1 zwischen zwei Geschwister-Teilbäumen entsteht, muss eine Rebalancierung durchgeführt werden. Dies muss innerhalb der aufgerufenen Funktion „automatisch“ (daher die Charakterisierung als ''self-balancing tree'' im Englischen) geschehen. Geeignete Reparaturwerkzeuge sind die sogenannten [[Binärbaum#Rotation in binären Bäumen|Rotationen]].


Für Einfügungen und Löschungen, bei denen die temporäre Höhendifferenz nie über 2 hinausgeht, werden zwei Varianten benötigt: Einfach- und Doppelrotation. Eine '''Einfachrotation''' leistet die Rebalancierung, wenn das ''innere Kind'' des um 2 höheren Geschwisters (''Z'' in den zwei Abbildungen 2 und 3), das ist das Kind mit einer Kindesrichtung, die der von ''Z'' entgegengesetzt ist, (''t<sub>23</sub>'' in der Abbildung 2 beziehungsweise ''Y'' in der Abbildung 3) ''nicht höher'' ist als sein Geschwister, das äußere Kind (''t<sub>4</sub>'' in beiden Abbildungen). Dieser Fall wird in der Literatur als Rechts-Rechts- (resp. gespiegelt Links-Links-)Situation bezeichnet, da ''X'' rechts- und ''Z'' nicht linkslastig ist, das heißt die zwei Balance-Faktoren 2 und 1 (beim Löschen auch 0) sind, kurz: die Balance zweimal hintereinander die gleiche Richtung hat. Der andere Fall, bei dem das innere Kind ''höher'' ist, wird von einer '''Doppelrotation''' abgedeckt – in der Literatur Rechts-Links- (resp. Links-Rechts-)Situation genannt, da ''X'' rechts- und ''Z'' linkslastig ist, das heißt die zwei Balance-Faktoren 2 und –1 sind, kurz: die Balance die Richtung wechselt.
Für Einfügungen und Löschungen, bei denen die temporäre Höhendifferenz nie über 2 hinausgeht, werden zwei Varianten benötigt: Einfach- und Doppelrotation. Eine '''Einfachrotation''' leistet die Rebalancierung, wenn das ''innere Kind'' des um 2 höheren Geschwisters (''Z'' in den zwei Abbildungen 2 und 3), das ist das Kind mit einer Kindesrichtung, die der von ''Z'' entgegengesetzt ist, (''t<sub>23</sub>'' in der Abbildung&nbsp;2 beziehungsweise ''Y'' in der Abbildung&nbsp;3) ''nicht höher'' ist als sein Geschwister, das äußere Kind (''t<sub>4</sub>'' in beiden Abbildungen). Dieser Fall wird in der Literatur als Rechts-Rechts- (resp. gespiegelt Links-Links-)Situation bezeichnet, da ''X'' rechts- und ''Z'' nicht linkslastig ist, das heißt die zwei Balance-Faktoren 2 und 1 (beim Löschen auch 0) sind, kurz: die Balance zweimal hintereinander die gleiche Richtung hat. Der andere Fall, bei dem das innere Kind ''höher'' ist, wird von einer '''Doppelrotation''' abgedeckt – in der Literatur Rechts-Links- (resp. Links-Rechts-)Situation genannt, da ''X'' rechts- und ''Z'' linkslastig ist, das heißt die zwei Balance-Faktoren 2 und –1 sind, kurz: die Balance die Richtung wechselt.


{{Anker|Abb_simple}}[[Bild:AVL-simple-left_v.svg|mini|182px|Abb. 2: Einfachrotation ''Links''(''X'')]]
{{Anker|Abb_simple}}[[Bild:AVL-simple-left_v.svg|mini|182px|Abb. 2: Einfachrotation ''Links''(''X'')]]
Das Prinzip der Rotation in Binärbäumen sei anhand der Abbildung 2 erläutert. Eine einzelne Rotation lässt sich durch die Rotationsrichtung ''Links'' oder ''Rechts'' und durch die Wurzel des betroffenen Teilbaums spezifizieren, zum Beispiel ''Links''(''X''). Sie bewirkt die Absenkung dieses ''X'' und die Anhebung eines anderen Knotens (hier: ''Z''). Insgesamt geht es um die folgenden 3 Aktionen:
Das Prinzip der Rotation in Binärbäumen sei anhand der Abbildung&nbsp;2 erläutert. Eine einzelne Rotation lässt sich durch die Rotationsrichtung ''Links'' oder ''Rechts'' und durch die Wurzel des betroffenen Teilbaums spezifizieren, zum Beispiel ''Links''(''X''). Sie bewirkt die Absenkung dieses ''X'' und die Anhebung eines anderen Knotens (hier: ''Z''). Insgesamt geht es um die folgenden 3 Aktionen:


:{| class="toptextcells"
:{| class="toptextcells"
|-
|-
| style="width:4.6em;" | unten: || Einhängen des zwischen ''Z'' und seinem [[Elternknoten|Elter]] ''X'' befindlichen Teilbaums (hier: des ''linken'' Kindes ''t<sub>23</sub>'' von ''Z'') als (hier: ''rechtes'') Kind bei ''X''.<ref group="Anm">''t<sub>23</sub>'' kann leer sein, was dem Fall ''h'' = 0 in der Abbildung 2 entspricht.</ref>
| style="width:4.6em;" | unten: || Einhängen des zwischen ''Z'' und seinem [[Elternknoten|Elter]] ''X'' befindlichen Teilbaums (hier: des ''linken'' Kindes ''t<sub>23</sub>'' von ''Z'') als (hier: ''rechtes'') Kind bei ''X''.<ref group="Anm">''t<sub>23</sub>'' kann leer sein, was dem Fall ''h'' = 0 in der Abbildung&nbsp;2 entspricht.</ref>
|-
|-
| Wippe: || Einhängen von ''X'' als (hier: ''linkes'') Kind bei ''Z''.
| Wippe: || Einhängen von ''X'' als (hier: ''linkes'') Kind bei ''Z''.
Zeile 220: Zeile 220:


=== {{Anker|Einfachrotation}}Einfachrotation im AVL-Baum ===
=== {{Anker|Einfachrotation}}Einfachrotation im AVL-Baum ===
Sie wird im Original: Малое вращение ''kleine Drehung'' genannt.
Sie wird im Original Малое вращение ''kleine Drehung'' genannt.


Die obenstehende Abbildung 2 zeigt eine Rechts-Rechts-Situation. In der oberen Hälfte haben die beiden Teilbäume ''Z'' und ''t<sub>1</sub>'' des Knotens ''X'' einen Höhenunterschied von <span style="color:#FF0000;">2</span>. Das innere Kind ''t<sub>23</sub>'' des um 2 höheren Knotens ''Z'' ist ''nicht höher'' als sein Geschwister ''t<sub>4</sub>''.<br>
Die obenstehende Abbildung&nbsp;2 zeigt eine Rechts-Rechts-Situation. In der oberen Hälfte haben die beiden Teilbäume ''Z'' und ''t<sub>1</sub>'' des Knotens ''X'' einen Höhenunterschied von <span style="color:#FF0000;">2</span>. Das innere Kind ''t<sub>23</sub>'' des um 2 höheren Knotens ''Z'' ist ''nicht höher'' als sein Geschwister ''t<sub>4</sub>''.<br>
<small>Dies kann nach einem Einfügen in den Teilbaum ''t<sub>4</sub>'' oder nach einem Löschen aus dem Teilbaum ''t<sub>1</sub>'' auftreten.
<small>Dies kann nach einem Einfügen in den Teilbaum ''t<sub>4</sub>'' oder nach einem Löschen aus dem Teilbaum ''t<sub>1</sub>'' auftreten.
{{Anker|EinfachrotationGleicheHöhe}}Der in der Abbildung 2 blass gehaltene Fall, dass ''t<sub>23</sub>'' gleich hoch ist wie ''t<sub>4</sub>'', kommt nur beim Löschen vor.</small>
{{Anker|EinfachrotationGleicheHöhe}}Der in der Abbildung&nbsp;2 blass gehaltene Fall, dass ''t<sub>23</sub>'' gleich hoch ist wie ''t<sub>4</sub>'', kommt nur beim Löschen vor.</small>


Die Rebalancierung (gezeigt in der unteren Hälfte der Abbildung) gelingt durch eine Einfachrotation nach links. Sie ist bereits oben als einzelne Rotation beschrieben. Die anzupassenden 3 Verknüpfungen sind in der Abbildung verstärkt gezeichnet.
Die Rebalancierung (gezeigt in der unteren Hälfte der Abbildung) gelingt durch eine Einfachrotation nach links. Sie ist bereits oben als einzelne Rotation beschrieben. Die anzupassenden 3 Verknüpfungen sind in der Abbildung verstärkt gezeichnet.
Zeile 232: Zeile 232:
Die gespiegelte, die Links-Links-Situation wird von einer Einfachrotation nach rechts behandelt.
Die gespiegelte, die Links-Links-Situation wird von einer Einfachrotation nach rechts behandelt.


[[Bild:AVL-double-rl_v.svg|mini|226px|Abb. 3: Doppelrotation ''RechtsLinks''(''Z'', ''X'')<br> = ''Rechts''(''Z'') dann ''Links''(''X'')]]
[[Bild:AVL-double-rl_v.svg|mini|229px|Abb. 3: Doppelrotation ''RechtsLinks''(''Z'', ''X'')<br> = ''Rechts''(''Z'') dann ''Links''(''X'')]]
=== {{Anker|Doppelrotation}}Doppelrotation im AVL-Baum ===
=== {{Anker|Doppelrotation}}Doppelrotation im AVL-Baum ===
Sie wird im Original: Большое вращение ''große Drehung'' genannt.
Sie wird im Original Большое вращение ''große Drehung'' genannt.


Die oben in der Abbildung 3 gezeigte Situation unterscheidet sich von der in Abbildung 2 darin, dass der mittlere Teilbaum (mit Wurzel ''Y''), das ist das innere Kind des um 2 höheren Knotens ''Z'', ''höher'' ist als sein Geschwister ''t<sub>4</sub>'' – eine Rechts-Links-Situation, da ''X'' rechts- und ''Z'' linkslastig ist.<br>
Die oben in der Abbildung&nbsp;3 gezeigte Situation unterscheidet sich von der in Abbildung&nbsp;2 darin, dass der mittlere Teilbaum (mit Wurzel ''Y''), das ist das innere Kind des um 2 höheren Knotens ''Z'', ''höher'' ist als sein Geschwister ''t<sub>4</sub>'' – eine Rechts-Links-Situation, da ''X'' rechts- und ''Z'' linkslastig ist.<br>
<small>Das kann nach einer Einfügung in einen der Teilbäume ''t<sub>2</sub>'' oder ''t<sub>3</sub>'' oder nach einer Löschung aus dem Teilbaum ''t<sub>1</sub>'' passieren.
<small>Das kann nach einer Einfügung in einen der Teilbäume ''t<sub>2</sub>'' oder ''t<sub>3</sub>'' oder nach einer Löschung aus dem Teilbaum ''t<sub>1</sub>'' passieren.
Der in der Abbildung 3 in Standardfarbe gehaltene Fall, bei dem ''t<sub>2</sub>'' gleich hoch ist wie ''t<sub>3</sub>'', kommt nur beim Löschen vor, während die 2 höhenungleichen Fälle (genau ein Teilbaum blass) sowohl beim Einfügen wie beim Löschen vorkommen. Der vierte Fall (zweimal blass) bedarf keiner Rebalancierung.</small>
Der in der Abbildung&nbsp;3 in Standardfarbe gehaltene Fall, bei dem ''t<sub>2</sub>'' gleich hoch ist wie ''t<sub>3</sub>'', kommt nur beim Löschen vor, während die 2 höhenungleichen Fälle (genau ein Teilbaum blass) sowohl beim Einfügen wie beim Löschen vorkommen. Der vierte Fall (zweimal blass) bedarf keiner Rebalancierung.</small>


Die Doppelrotation, deren Ergebnis im unteren Drittel der Abb. gezeigt ist, ist eine Rechtsrotation durch ''Z'' (gegen die Linkslastigkeit von ''Z'', mittleres Drittel) gefolgt von einer Linksrotation durch ''X'' (gegen die Rechtslastigkeit von ''X''). Sie bewirkt eine zweimalige Anhebung des höheren (und inneren) Kindes ''Y'' von ''Z''.
Die Doppelrotation, deren Ergebnis im unteren Drittel der Abb. gezeigt ist, ist eine Rechtsrotation durch ''Z'' (gegen die Linkslastigkeit von ''Z'', mittleres Drittel) gefolgt von einer Linksrotation durch ''X'' (gegen die Rechtslastigkeit von ''X''). Sie bewirkt eine zweimalige Anhebung des höheren (und inneren) Kindes ''Y'' von ''Z''.


Die anzupassenden 5 Verknüpfungen sind in der Abb. verstärkt gezeichnet.
Die anzupassenden 5 Verknüpfungen sind in der Abbildung verstärkt gezeichnet.


Die Höhe des neuen Teilbaums ''Y'' ist nach einer Einfügung gleich wie die von ''X'' vor der Operation, während sie nach einer Löschung um 1 vermindert ist.
Die Höhe des neuen Teilbaums ''Y'' ist nach einer Einfügung gleich wie die von ''X'' vor der Operation, während sie nach einer Löschung um 1 vermindert ist.
Zeile 249: Zeile 249:


=== Beispiel ===
=== Beispiel ===
Der AVL-Baum in der Abbildung 1 wird
Der AVL-Baum in der Abbildung&nbsp;1 wird
* nach der Löschung des Knotens '''G''' durch die zwei Einfachrotationen ''Rechts''('''F'''), später ''Links''('''J''')
* nach der Löschung des Knotens '''G''' durch die zwei Einfachrotationen ''Rechts''('''F'''), später ''Links''('''J''')
* nach der Einfügung eines Knotens '''T''' durch die Doppelrotation ''RechtsLinks''('''V''',&nbsp;'''P''')
* nach der Einfügung eines Knotens '''T''' durch die Doppelrotation ''RechtsLinks''('''V''',&nbsp;'''P''')
Zeile 257: Zeile 257:


=== Verketten ===
=== Verketten ===
[[File:AVL-concat.svg|mini|left|117px|Abb. 4: Verkettung von 2 AVL-Bäumen]]Zwei AVL-Bäume können mit logarithmischem Aufwand verkettet (konkateniert) werden (englisch: ''concat'' oder auch nur ''cat''). Für die Sortierfolge müssen natürlich alle Schlüssel des ersten Baums allen Schlüsseln des zweiten vorangehen.<ref group="Anm">'''Algorithmus:''' Man steigt an der rechten Flanke des ersten Baums (siehe Abbildung 4. Die grauen Pfeile zeigen den Weg durch den Graphen.) und an der linken des zweiten bis zu den Blättern hinab und merkt sich die Knoten auf den Pfaden zusammen mit ihren Höhen.<br>Ohne Beschränkung der Allgemeinheit sei der erste Baum der höhere (wie in der Abbildung). Dem zweiten Baum wird sein erstes Element ''H'' entnommen. Es spielt nachher die Rolle eines „Bindeglieds“. Die Höhe des zweiten Baums sei jetzt ''h''. Man sucht nun in der rechten Flanke des ersten Baums nach einem Knoten ''G'' der Höhe ''h''+1 oder ''h'' (einen von beiden muss es geben; gibt es beide, sei der höhere bevorzugt). Man macht den zweiten Baum zum Geschwister von ''G'', wobei das Bindeglied ''H'' als Elter von beiden eingesetzt wird. Dann wird ''H'' beim Elter ''F'' von ''G'' an ''G''s Stelle als Kind eingehängt. Der maximale Höhenunterschied zwischen ''E'' und dem neuen ''H'' ist 2 (wenn ''E'' die Höhe ''h''–1 hat, dann hat ''G'' die Höhe ''h'', und das neue ''H'' die Höhe ''h''+1). Nach einer ersten Anpassung mit eventueller (Doppel-)Rotation müssen noch aufsteigend die Balance-Faktoren [[#EinfügenÜ|wie bei einer Einfügung überprüft]] und gegebenenfalls korrigiert werden. <br>Dabei kann die Höhe des Baums um 1 zunehmen.</ref>
[[File:AVL-concat.svg|mini|left|117px|Abb. 4: Verkettung von 2 AVL-Bäumen]]Zwei AVL-Bäume können mit logarithmischem Aufwand verkettet (konkateniert) werden (englisch: ''concat'' oder auch nur ''cat''). Für die Sortierfolge müssen natürlich alle Schlüssel des ersten Baums allen Schlüsseln des zweiten vorangehen.<ref group="Anm">'''Algorithmus:''' Man steigt an der rechten Flanke des ersten Baums (siehe Abbildung&nbsp;4. Die grauen Pfeile zeigen den Weg durch den Graphen.) und an der linken des zweiten bis zu den Blättern hinab und merkt sich die Knoten auf den Pfaden zusammen mit ihren Höhen.<br>Ohne Beschränkung der Allgemeinheit sei der erste Baum der höhere (wie in der Abbildung). Dem zweiten Baum wird sein erstes Element ''H'' entnommen. Es spielt nachher die Rolle eines „Bindeglieds“. Die Höhe des zweiten Baums sei jetzt ''h''. Man sucht nun in der rechten Flanke des ersten Baums nach einem Knoten ''G'' der Höhe ''h''+1 oder ''h'' (einen von beiden muss es geben; gibt es beide, sei der höhere bevorzugt). Man macht den zweiten Baum zum Geschwister von ''G'', wobei das Bindeglied ''H'' als Elter von beiden eingesetzt wird. Dann wird ''H'' beim Elter ''F'' von ''G'' an ''G''s Stelle als Kind eingehängt. Der maximale Höhenunterschied zwischen ''E'' und dem neuen ''H'' ist 2 (wenn ''E'' die Höhe ''h''–1 hat, dann hat ''G'' die Höhe ''h'', und das neue ''H'' die Höhe ''h''+1). Nach einer ersten Anpassung mit eventueller (Doppel-)Rotation müssen noch aufsteigend die Balance-Faktoren [[#EinfügenÜ|wie bei einer Einfügung überprüft]] und gegebenenfalls korrigiert werden. <br>Dabei kann die Höhe des Baums um 1 zunehmen.</ref>


=== Spalten ===
=== Spalten ===
{{Anker|Abb_Spalten}}[[File:AVL-split.svg|mini|right|125px|Abb. 5: Ein Indukti-<br>onsschritt beim Spalten eines AVL-Baums]]
{{Anker|Abb_Spalten}}[[File:AVL-split.svg|mini|right|125px|Abb. 5: Ein Indukti-<br>onsschritt beim Spalten eines AVL-Baums]]
Etwas komplizierter als das Verketten ist das Spalten (englisch: ''split'') eines AVL-Baums in zwei separate AVL-Bäume an einem externen Knoten, also einer Stelle ''zwischen'' zwei internen Knoten (Schlüsseln), die als Paar (Knoten, Richtung) einschließlich des Pfades zur Wurzel gegeben sei. Links davon liegt die linke Partition mit den kleineren Schlüsseln und rechts davon die rechte mit den größeren. Die so spezifizierte Trennlinie (rot in der Abbildung 5) zerschneidet Kanten des gegebenen Baums auf dem Pfad des Knotens zur Wurzel, so dass sich links wie rechts ein [[Wald (Graphentheorie)|Wald]] von „Schnipseln“ ergibt. Schließlich kann jeder der beiden Wälder mit logarithmischem Aufwand zu einem AVL-Baum zusammengefügt werden.<ref group="Anm">Die Schnipsel sind Bäume in AVL-Form bis auf eventuell einen Knoten („Stummel“) auf hoher Ebene mit Ausgangsgrad 1 (nicht unbedingt die Wurzel des Schnipsels), der beim Verketten mit dem nächstniedrigeren Schnipsel auf der gleichen Seite das Bindeglied spielt.
Etwas komplizierter als das Verketten ist das Spalten (englisch: ''split'') eines AVL-Baums in zwei separate AVL-Bäume an einem externen Knoten, also einer Stelle ''zwischen'' zwei internen Knoten (Schlüsseln), die als Paar (Knoten, Richtung) einschließlich des Pfades zur Wurzel gegeben sei. Links davon liegt die linke Partition mit den kleineren Schlüsseln und rechts davon die rechte mit den größeren. Die so spezifizierte Trennlinie (rot in der Abbildung&nbsp;5) zerschneidet Kanten des gegebenen Baums auf dem Pfad des Knotens zur Wurzel, so dass sich links wie rechts ein [[Wald (Graphentheorie)|Wald]] von „Schnipseln“ ergibt. Schließlich kann jeder der beiden Wälder mit logarithmischem Aufwand zu einem AVL-Baum zusammengefügt werden.<ref group="Anm">Die Schnipsel sind Bäume in AVL-Form bis auf eventuell einen Knoten („Stummel“) auf hoher Ebene mit Ausgangsgrad 1 (nicht unbedingt die Wurzel des Schnipsels), der beim Verketten mit dem nächstniedrigeren Schnipsel auf der gleichen Seite das Bindeglied spielt.


'''Algorithmus''' ([[vollständige Induktion]] nach den Schnipseln, auszuführen auf beiden Seiten der Trennlinie): Gibt es ein Kind des ''gegebenen'' Knotens in der ''gegebenen'' Richtung, dann stellt der Teilbaum dieses Kindes einen Schnipsel dar, welcher schon in AVL-Form ist. Der gegebene Knoten liegt auf der Gegenseite und ist zum Stummel geworden. Er wird in den Teilbaum seines nunmehr „Einzelkindes“ je nach Seite als rechtestes beziehungsweise linkestes Element einfügt. Auf beiden Seiten ist der niedrigste Schnipsel jetzt ein AVL-Baum. Damit ist der Induktionsanfang auf beiden Seiten geschafft. <br>Induktionsschritt (siehe [[#Abb_Spalten| Abbildung 5]]): Der niedrigste Schnipsel von beiden Seiten ist nach Induktionsannahme ein AVL-Baum. [[Ohne Beschränkung der Allgemeinheit]] sei er links und heiße ''I''. Beim Aufstieg im Pfad gelangt man zum nächsthöheren ebenfalls linken Stummel ''H'', dessen ursprüngliche Höhe mit ''h'' bezeichnet sei. Zerschnittene Kanten (gestrichelte schwarze Pfeile in der Abbildung) und die von ihnen zurück gelassenen Stummel erkennt man am Wechsel der Kindesrichtungen beim Aufstieg im Pfad. Die Höhen sind anhand der Balance-Faktoren aufs genaueste mitzuschreiben. Der Teilbaum des Einzelkindes ''D'' von ''H'' wird mit dem Schnipsel ''I'' unter Einsatz von ''H'' als Bindeglied [[#Verketten|verkettet]]. Dabei kann seine Höhe um 1 zunehmen. Darauf macht man ''D'' zum Kind seines vormaligen Großelters ''C'' in der Kindesrichtung seines vormaligen Elters ''H'', das heißt zum Geschwister seines vormaligen Onkels ''B''. Die Höhendifferenz zu letzterem ist maximal 3. Die lokale AVL-Balance lässt sich durch Rotationen herstellen, wobei die Höhe des Teilbaums um 1 abnehmen kann. (Die Vorgehensweise bei einem Höhenunterschied von 3 ähnelt der bei einem von 2: Ist das innere Kind des höheren Geschwisters niedriger als das äußere Kind, kann man die AVL-Balance mit einer [[#Einfachrotation|Einfachrotation]] herstellen. Ist es höher, kommt man mit einer [[#Doppelrotation|Doppelrotation]] durch. Sind beide Kinder gleich hoch, hängt es vom Balance-Faktor des inneren Kindes ab: ist der ausgewogen, macht’s eine Doppelrotation; ist er außenlastig, schaffen’s zwei Rotationen; ist er innenlastig, geht’s mit drei Rotationen.) Bei den Ebenen darüber sind die Balance-Faktoren [[#LöschenÜ|wie bei einer Löschung zu überprüfen]] und gegebenenfalls anzupassen, wobei die Gesamthöhe wieder um 1 abnehmen kann. Damit ist ''I'' so in den neuen Schnipsel ''A'' integriert, dass ''A'' wieder ein AVL-Baum ist – und den Schnipsel ''I'' des nächsten Rekursionsschrittes darstellt. <br>Die innerhalb eines Rekursionsschrittes besuchten Kanten (graue Pfeile in der Abbildung 5) betreffen nur Ebenen zwischen den Wurzeln von zwei Schnipseln, die auf derselben Seite übereinander liegen. Die Rekursionsschritte auf der linken und rechten Seite greifen reißverschlussartig ineinander: der Aufstieg auf der rechten wird auf der linken Seite beim Verketten ab- und wieder aufgestiegen, woran sich ein Aufstieg anschließt, der auf der rechten Seite ab- und wieder aufgestiegen wird, usw. Eine Ebene wird höchstens dreimal besucht, jedes Mal mit konstantem Aufwand. Damit ist der Gesamtaufwand fürs Spalten proportional zur Gesamthöhe.</ref><ref>Costin S, der in seinem Projekt [[#Costin S|AvlTree.cs]] AVL-Bäume mit Concat- und Splitoperationen implementiert, fordert dazu die Aufzeichnung der vollen absoluten Höhe in jedem Knoten. Man kann aber – wie gezeigt – mit den Balance-Faktoren auskommen, ohne den Aufwand zu erhöhen.</ref>
'''Algorithmus''' ([[vollständige Induktion]] nach den Schnipseln, auszuführen auf beiden Seiten der Trennlinie): Gibt es ein Kind des ''gegebenen'' Knotens in der ''gegebenen'' Richtung, dann stellt der Teilbaum dieses Kindes einen Schnipsel dar, welcher schon in AVL-Form ist. Der gegebene Knoten liegt auf der Gegenseite und ist zum Stummel geworden. Er wird in den Teilbaum seines nunmehr „Einzelkindes“ je nach Seite als rechtestes beziehungsweise linkestes Element einfügt. Auf beiden Seiten ist der niedrigste Schnipsel jetzt ein AVL-Baum. Damit ist der Induktionsanfang auf beiden Seiten geschafft. <br>Induktionsschritt (siehe [[#Abb_Spalten| Abbildung&nbsp;5]]): Der niedrigste Schnipsel von beiden Seiten ist nach Induktionsannahme ein AVL-Baum. [[Ohne Beschränkung der Allgemeinheit]] sei er links und heiße ''I''. Beim Aufstieg im Pfad gelangt man zum nächsthöheren ebenfalls linken Stummel ''H'', dessen ursprüngliche Höhe mit ''h'' bezeichnet sei. Zerschnittene Kanten (gestrichelte schwarze Pfeile in der Abbildung) und die von ihnen zurück gelassenen Stummel erkennt man am Wechsel der Kindesrichtungen beim Aufstieg im Pfad. Die Höhen sind anhand der Balance-Faktoren aufs genaueste mitzuschreiben. Der Teilbaum des Einzelkindes ''D'' von ''H'' wird mit dem Schnipsel ''I'' unter Einsatz von ''H'' als Bindeglied [[#Verketten|verkettet]]. Dabei kann seine Höhe um 1 zunehmen. Darauf macht man ''D'' zum Kind seines vormaligen Großelters ''C'' in der Kindesrichtung seines vormaligen Elters ''H'', das heißt zum Geschwister seines vormaligen Onkels ''B''. Die Höhendifferenz zu letzterem ist maximal 3. Die lokale AVL-Balance lässt sich durch Rotationen herstellen, wobei die Höhe des Teilbaums um 1 abnehmen kann. (Die Vorgehensweise bei einem Höhenunterschied von 3 ähnelt der bei einem von 2: Ist das innere Kind des höheren Geschwisters niedriger als das äußere Kind, kann man die AVL-Balance mit einer [[#Einfachrotation|Einfachrotation]] herstellen. Ist es höher, kommt man mit einer [[#Doppelrotation|Doppelrotation]] durch. Sind beide Kinder gleich hoch, hängt es vom Balance-Faktor des inneren Kindes ab: ist der ausgewogen, macht’s eine Doppelrotation; ist er außenlastig, schaffen’s zwei Rotationen; ist er innenlastig, geht’s mit drei Rotationen.) Bei den Ebenen darüber sind die Balance-Faktoren [[#LöschenÜ|wie bei einer Löschung zu überprüfen]] und gegebenenfalls anzupassen, wobei die Gesamthöhe wieder um 1 abnehmen kann. Damit ist ''I'' so in den neuen Schnipsel ''A'' integriert, dass ''A'' wieder ein AVL-Baum ist – und den Schnipsel ''I'' des nächsten Rekursionsschrittes darstellt. <br>Die innerhalb eines Rekursionsschrittes besuchten Kanten (graue Pfeile in der Abbildung&nbsp;5) betreffen nur Ebenen zwischen den Wurzeln von zwei Schnipseln, die auf derselben Seite übereinander liegen. Die Rekursionsschritte auf der linken und rechten Seite greifen reißverschlussartig ineinander: der Aufstieg auf der rechten wird auf der linken Seite beim Verketten ab- und wieder aufgestiegen, woran sich ein Aufstieg anschließt, der auf der rechten Seite ab- und wieder aufgestiegen wird, usw. Eine Ebene wird höchstens dreimal besucht, jedes Mal mit konstantem Aufwand. Damit ist der Gesamtaufwand fürs Spalten proportional zur Gesamthöhe.</ref><ref>Costin S, der in seinem Projekt [[#Costin S|AvlTree.cs]] AVL-Bäume mit Concat- und Splitoperationen implementiert, fordert dazu die Aufzeichnung der vollen absoluten Höhe in jedem Knoten. Man kann aber – wie gezeigt – mit den Balance-Faktoren auskommen, ohne den Aufwand zu erhöhen.</ref>


=== Anwendungsbeispiele ===
=== Anwendungsbeispiele ===
Zeile 323: Zeile 323:
Der Platzbedarf und das Laufzeitverhalten für die beschriebenen Operationen verhalten sich im Mittel und im Worst Case identisch. Zwar bietet der Rot-Schwarz-Baum [[amortisierte Laufzeitanalyse|''amortisiert konstante'']] Modifikationskosten<ref>{{BibISBN|9783540779773}} S. 165.</ref> und der AVL-Baum nur ''im Mittel konstante''.<ref group="Anm">Fügt man ''n'' Knoten in einen leeren AVL-Baum ein, so ist (wie beim Rot-Schwarz-Baum) der Aufwand ''A'' im Mittel proportional zu ''n''. Es gibt aber auch Schlüsselfolgen, bei denen ''A'' schneller wächst, und zwar in etwa wie die Anzahl der Vergleiche, die für die Positionierung dieser Einfügungen nötig sind.</ref> Anwendungen von binären Suchbäumen, die nur Sequenzen von Einfügungen und Löschungen – aber keine intervenierenden Suchoperationen – enthalten, nutzen den Zweck des Suchbaums nicht aus. Sieht man also von dieser Art Anwendungen ab, ist das Gesamtverhalten einer Mischung von Suchen und Modifikation bei beiden Datenstrukturen amortisiert, im Mittel und im Worst Case logarithmisch.
Der Platzbedarf und das Laufzeitverhalten für die beschriebenen Operationen verhalten sich im Mittel und im Worst Case identisch. Zwar bietet der Rot-Schwarz-Baum [[amortisierte Laufzeitanalyse|''amortisiert konstante'']] Modifikationskosten<ref>{{BibISBN|9783540779773}} S. 165.</ref> und der AVL-Baum nur ''im Mittel konstante''.<ref group="Anm">Fügt man ''n'' Knoten in einen leeren AVL-Baum ein, so ist (wie beim Rot-Schwarz-Baum) der Aufwand ''A'' im Mittel proportional zu ''n''. Es gibt aber auch Schlüsselfolgen, bei denen ''A'' schneller wächst, und zwar in etwa wie die Anzahl der Vergleiche, die für die Positionierung dieser Einfügungen nötig sind.</ref> Anwendungen von binären Suchbäumen, die nur Sequenzen von Einfügungen und Löschungen – aber keine intervenierenden Suchoperationen – enthalten, nutzen den Zweck des Suchbaums nicht aus. Sieht man also von dieser Art Anwendungen ab, ist das Gesamtverhalten einer Mischung von Suchen und Modifikation bei beiden Datenstrukturen amortisiert, im Mittel und im Worst Case logarithmisch.


Realistische Anwendungssituationen mit Performancedaten und -vergleichen – auch mit weiteren Suchalgorithmen und Spielarten der Datenstrukturen – finden sich bei Ben Pfaff<ref>in Ben Pfaff: ''Performance Analysis of BSTs in System Software.'' Stanford University 2004.</ref>. Seine Ergebnisse zeigen in 79 Messungen unter anderem die sehr große Ähnlichkeit von AVL-Bäumen (''AVL'') und Rot-Schwarz-Bäumen (''RB'') mit Laufzeitverhältnissen {{bruch|''AVL''|''RB''}} zwischen 0,677 und 1,077 bei einem [[Median]] von ≈0,947 und einem [[Geometrisches Mittel|geometrischen Mittelwert]] von ≈0,910.
Realistische Anwendungssituationen mit Performancedaten und -vergleichen – auch mit weiteren Suchalgorithmen und Spielarten der Datenstrukturen – finden sich bei Ben Pfaff<ref name="Pfaff_b">Ben Pfaff: ''Performance Analysis of BSTs in System Software.'' Stanford University 2004.</ref>. Seine Ergebnisse zeigen in 79 Messungen unter anderem die sehr große Ähnlichkeit von AVL-Bäumen (''AVL'') und Rot-Schwarz-Bäumen (''RB'') mit Laufzeitverhältnissen {{bruch|''AVL''|''RB''}} zwischen 0,677 und 1,077 bei einem [[Median]] von ≈0,947 und einem [[Geometrisches Mittel|geometrischen Mittelwert]] von ≈0,910.


== Anwendungen ==
== Anwendungen ==
Wie Ben Pfaff<ref>in Ben Pfaff: ''Performance Analysis of BSTs in System Software.'' Stanford University 2004.</ref> zeigt, sind die dynamischen Suchbaumstrukturen AVL-Baum, Rot-Schwarz-Baum und Splay-Baum funktionell austauschbar. Sie unterscheiden sich nur in ihrem Laufzeitverhalten. Bei den Messungen von Ben Pfaff schneidet der AVL-Baum in Median und Mittelwert am besten ab.
Wie Ben Pfaff<ref name="Pfaff_b" /> zeigt, decken die dynamischen Suchbaumstrukturen AVL-Baum, [[Rot-Schwarz-Baum]] und [[Splay-Baum]] dieselben wesentlichen Funktionen ab. Große Unterschiede stellt er im Laufzeitverhalten fest, wobei der AVL-Baum in Median und Mittelwert am besten abschneidet.

Im täglichen Leben kommt es auf punktgenaue Zeitgerechtigkeit selten an. Und wenn doch, muss man mit anderen Verfahren arbeiten als mit dynamischen Suchbaumstrukturen. Insofern werden diese ganz vorwiegend für systeminterne Zwecke eingesetzt.


In der Informatik haben die dynamischen Suchbaumstrukturen einen großen Einsatzbereich als grundlegende Hilfsmittel bei:
* Duplikatunterdrückung, [[Deduplizierung]], [[Duplikaterkennung]]
* Elementare [[Menge (Mathematik)|Mengenoperationen]] wie Durchschnittsbildung und Vereinigung
* [[Standard Template Library]] (STL)
In diesem Artikel sind erwähnt:
In diesem Artikel sind erwähnt:
* in der [[#Motivation]] die Liste der Variablen in einem Programm, die ein [[Interpreter]] oder [[Compiler]] zu pflegen hat.
* in der [[#Motivation]] die Liste der Variablen in einem Programm, die ein [[Interpreter]] oder [[Compiler]] zu pflegen hat.
* im Abschnitt [[#Beispiel einer Anwendung mit mehreren AVL-Zugriffspfaden]] ein klassisches Speichermanagement.
* im Abschnitt [[#Beispiel einer Anwendung mit mehreren AVL-Zugriffspfaden]] ein klassisches Speichermanagement.
Bei Ben Pfaff<ref>in Ben Pfaff: ''Performance Analysis of BSTs in System Software.'' Stanford University 2004.</ref> finden sich weitere (ähnliche) Anwendungen (alle unter x86-based Linux) wie:
Bei Ben Pfaff<ref name="Pfaff_b" /> finden sich weitere (ähnliche) Anwendungen (alle unter x86-based Linux):
* Verwaltung von Virtual Memory Areas (VMAs) unter Einschluss von ''range queries'' zur Feststellung des Überlappens von existierenden VMAs (S. 4).
* Verwaltung von Virtual Memory Areas (VMAs) unter Einschluss von ''range queries'' zur Feststellung des Überlappens von existierenden VMAs (S. 4).
* eindeutige Kennzeichnung von IP-Paketen (S. 7).
* Eindeutige Kennzeichnung von IP-Paketen (S. 7).


== Historisches ==
== Historisches ==
Wie im Abschnitt [[#Motivation]] erwähnt, kann die sehr bekannte Suchstruktur [[Binäre Suche|Binäre Suche im Array]] als Vorläufer der dynamischen Suchbaumstrukturen gelten. Als naheliegende Umsetzung des Nachschlagens in einem (sortierten) Wörterbuch, dürfte sie mehrfach ohne Kenntnis anderer Implementierungen entwickelt und implementiert worden sein. Sie schneidet allerdings im dynamischen Anwendungsfall vergleichweise schlecht ab, obwohl sie im statischen Fall eine hervorragende Lösung ist. Es gibt [[Makro]]s, die einen [[Compiler]] veranlassen, aus einer Tabelle von (Argument, Werte)-Paaren [[Instruktion#Programmierbefehl|Instruktionen]] für eine fertige binäre Suche zu erzeugen.
Wie im Abschnitt [[#Motivation]] erwähnt, kann die sehr bekannte Suchstruktur [[Binäre Suche|Binäre Suche im Array]] als Vorläufer der dynamischen Suchbaumstrukturen gelten. Als naheliegende Umsetzung des Nachschlagens in einem (sortierten) Wörterbuch, dürfte sie mehrfach ohne Kenntnis anderer Implementierungen entwickelt und implementiert worden sein. Im dynamischen Anwendungsfall kann sie aber mit den neueren Entwicklungen nicht mithalten, obwohl sie im statischen Fall eine hervorragende Lösung ist. Es gibt [[Makro]]s, die einen [[Compiler]] veranlassen, zu einer gegebenen Tabelle von (Argument, Werte)-Paaren [[Quelltext]] ohne Programmschleife für eine binäre Suche zu erzeugen.

Im Jahr 1962 erschien zum ersten Mal eine dynamische Suchbaumstruktur in Form des AVL-Baums. Seine Erfinder sind die genannten sowjetischen Mathematiker Georgi Adelson-Velski und Jewgeni Landis. Ihr Beitrag im Journal ''Doklady Akademii Nauk SSSR'' wurde schon im gleichen Jahr ins Englische übersetzt. Die Übersetzung trägt (wie entsprechend das Original) den sehr ehrgeizigen Titel „An algorithm for the organization of information“. Der Name AVL-Baum findet sich in dieser Übersetzung noch nicht.

Im Jahr 1970 veröffentlichte Rudolf Bayer<ref>{{Literatur
| Autor=[[Rudolf Bayer]], Edward M. McCreight
| Titel=Organization and Maintenance of Large Ordered Indices
| Verlag= Boeing Scientific Research Laboratories
| Jahr=1970
| Band= Mathematical and Information Sciences Report No. 20
}}</ref> seine erste Arbeit über den [[B-Baum]]. Er ist kein Binärbaum und unterstützt heterogene Speicher, beispielsweise Hauptspeicher und Hintergrundspeicher, und wird bei [[Datenbanksystem|Datenbanksystemen]] eingesetzt.


Danach folgte im Jahr 1972 zunächst unter dem Namen „symmetric binary B-tree“ der [[Rot-Schwarz-Baum]] von [[Rudolf Bayer]]<ref>{{cite journal
Im Jahr 1962 erschien zum ersten Mal eine dynamische Suchbaumstruktur in Form des AVL-Baums. Seine Erfinder sind die genannten sowjetischen Mathematikern Georgi Adelson-Velski und Jewgeni Landis. Ihr Beitrag im Journal ''Doklady Akademii Nauk SSSR'' wurde schon im gleichen Jahr ins Englische übersetzt. Die Übersetzung trägt (wie entsprechend das Original) den sehr ehrgeizigen Titel „An algorithm for the organization of information“. Die Namensgebung AVL-Baum findet sich in dieser Übersetzung noch nicht.
| author=Rudolf Bayer
| title=Symmetric binary B-Trees: Data structure and maintenance algorithms
| journal=Acta Informatica
| volume=1
| issue=4
| year=1972
| pages=290–306
| url=http://www.springerlink.com/content/qh51m2014673513j/
| doi=10.1007/BF00289509
}}</ref>. Ihm war die Balance-Regel des AVL-Baums zu streng. Eine Umbenennung erfolgte 1978 von Leonidas Guibas und [[Robert Sedgewick]]<ref>{{cite conference
| author=Leonidas J. Guibas, [[Robert Sedgewick]]
| title=A Dichromatic Framework for Balanced Trees
| booktitle=Proceedings of the 19th Annual Symposium on Foundations of Computer Science
| year=1978
| pages=8–21
| url=http://doi.ieeecomputersociety.org/10.1109/SFCS.1978.3
| doi=10.1109/SFCS.1978.3}}</ref> in das heute übliche „red–black tree“, später auch „RB tree“.


[[Splay-Baum|Splay-Bäume]] wurden im Jahr 1985 von Daniel Sleator und [[Robert Tarjan]]<ref>{{Literatur
Danach folgten im Jahr 1972 zunächst unter dem Namen „symmetric binary B-trees“ die [[Rot-Schwarz-Baum|Rot-Schwarz-Bäume]] von [[Rudolf Bayer]]. Ihm waren die Balancierungs-Regeln des AVL-Baums zu streng. Eine Umbenennung erfolgte 1978 von Leo Guibas und Robert Sedgewick in das heute übliche „red–black tree“.
| Autor=Daniel D. Sleator, [[Robert E. Tarjan|Robert Tarjan]]
| Titel=[http://www.cs.cmu.edu/~sleator/papers/self-adjusting.pdf Self-Adjusting Binary Search Trees] (PDF; 6,1&nbsp;MB)
| Sammelwerk=Journal of the ACM ([[Association for Computing Machinery]])
| Band=32
| Nummer=3
| Seiten=652–686
| Jahr= 1985
| DOI=10.1145/3828.3835 }}</ref> unter dem Namen „Self-Adjusting Binary Search Trees“ vorgestellt. Sie sind noch dynamischer als die vorgenannten, indem sie sich auch bei Suchoperationen verändern.


Eine grobe Gegenüberstellung dynamischer Suchbäume findet sich im
[[Splay-Baum|Splay-Bäume]] wurden im Jahr 1985 von Daniel Sleator und [[Robert Tarjan]] unter dem Namen „Self-Adjusting Binary Search Trees“ vorgestellt. Sie sind noch dynamischer als die vorgenannten, indem sie sich auch bei Suchoperationen verändern.
{{Hauptartikel|Suchbaum}}


== Weiterführende Informationen ==
== Weiterführende Informationen ==
Zeile 359: Zeile 397:
* {{Anker|Cormen}}{{BibISBN|0262032937}}
* {{Anker|Cormen}}{{BibISBN|0262032937}}
* Ralf Hartmut Güting, Stefan Dieker: ''Datenstrukturen und Algorithmen.'' Stuttgart 2004, ISBN 9783519221210.
* Ralf Hartmut Güting, Stefan Dieker: ''Datenstrukturen und Algorithmen.'' Stuttgart 2004, ISBN 9783519221210.
* {{Anker|Knuth}}{{Literatur|Autor=[[Donald Knuth|Donald E. Knuth]]|Titel=[[The Art of Computer Programming]], Volume 3, Sorting and Searching, Second Edition|Verlag=Addison-Wesley|Ort=|Jahr=1998|ISBN=0-201-89685-0|Kapitel=6.2.3 Balanced Trees|Seiten=458–478}}
* {{Anker|Knuth}}{{Literatur
| Autor=[[Donald Knuth
| Donald E. Knuth]]
| Titel=[[The Art of Computer Programming]], Volume 3, Sorting and Searching, Second Edition
| Verlag=Addison-Wesley
| Ort=
| Jahr=1998
| ISBN=0-201-89685-0
| Kapitel=6.2.3 Balanced Trees
| Seiten=458–478
}}
* Udi Manber: ''Introduction to Algorithms – A Creative Approach.'' Addison-Wesley Publishing Company 1989, Kapitel 4.3.4, S. 75ff.
* Udi Manber: ''Introduction to Algorithms – A Creative Approach.'' Addison-Wesley Publishing Company 1989, Kapitel 4.3.4, S. 75ff.
* {{Anker|Mehlhorn 1988}}[[Kurt Mehlhorn]]: ''Datenstrukturen und effiziente Algorithmen.'' Teubner Stuttgart 1988, ISBN 3-519-12255-3.
* {{Anker|Mehlhorn 1988}}[[Kurt Mehlhorn]]: ''Datenstrukturen und effiziente Algorithmen.'' Teubner Stuttgart 1988, ISBN 3-519-12255-3.

Version vom 1. Februar 2014, 12:21 Uhr

Abb. 1: AVL-Baum mit Balance-Faktoren (grün)
AVL-Baum
Komplexität
Platz
Operation im Mittel Worst Case
Suchen
Querschritt
Min, Max
Einfügen
Löschen
Verketten
Spalten
  Knoten im Baum
  ohne Navigation
Tab. 1: Platz- und Zeit-Komplexitäten

Der AVL-Baum ist eine Datenstruktur in der Informatik, und zwar ein binärer Suchbaum mit der zusätzlichen Eigenschaft, dass sich an jedem Knoten die Höhe der beiden Teilbäume um höchstens 1 unterscheidet.[1] Diese Eigenschaft macht ihn zu einem balancierten binären Suchbaum und garantiert bei einer Belegung mit Schlüsseln eine Höhe , die logarithmisch in ist. Ebenso logarithmisch verhält sich die maximale Anzahl der Schritte (Vergleiche), die nötig sind, um An- oder Abwesenheit eines Schlüssels festzustellen, sowie der maximale Aufwand beim Einfügen und Löschen.

Der AVL-Baum ist benannt nach den sowjetischen Mathematikern Georgi Maximowitsch Adelson-Velski und Jewgeni Michailowitsch Landis, die die Datenstruktur im Jahr 1962 vorstellten.[2] Damit ist der AVL-Baum die älteste Datenstruktur für balancierte Bäume.

Motivation

Suchbäume sind Lösungen des so genannten „Wörterbuchproblems“. Angenommen ist eine große Anzahl von Schlüsseln, denen jeweils ein Wert beigegeben ist. In einem Wörterbuch deutsch–englisch ist das deutsche Wort der Schlüssel und das englische Wort der gesuchte Wert bzw. beigegebene englische Wörter sind die gesuchten Werte. Ähnlich verhält es sich bei einem Telefonbuch mit Namen und Adresse als Schlüssel und der Telefonnummer als dem gesuchten Wert.

Mathematisch gesehen realisiert ein Wörterbuch eine (endliche) Funktion, mit dem Schlüssel als „Argument“ und dem Wert als „Funktionswert“. Es ordnet einem Argument einen (aus seiner Sicht) eindeutigen Funktionswert zu.

Bei beiden Beispielen sind üblicherweise die Schlüssel sortiert. Das ist sehr zweckmäßig, es erlaubt nämlich, das Buch in der Mitte aufzuschlagen, zu schauen, ob der Schlüssel gefunden ist; und wenn nicht, ob man besser weiter vorn oder weiter hinten weitersucht.

Dieses Verfahren, welches nach dem Prinzip Teile und herrsche vorgeht, hat große Vorteile in der Geschwindigkeit des Auffindens von gesuchten Werten gegenüber einem, bei dem ein ganzes Buch unter Umständen von vorn bis hinten durchsucht werden muss. Es hat in der Informatik den Namen „binäres Suchen“, während letzteres als „sequentielles Suchen“ bezeichnet wird.

Eine nahe liegende Datenstruktur für binäres Suchen ist der binäre Suchbaum. Sie ist ausführlich beschrieben im

Die Höhe h eines Baums ist die Länge des längsten Wegs von der Wurzel zu einem Blatt. Bei einem binären Suchbaum hängen viele Leistungsindikatoren eng mit der Höhe zusammen – sie stimmt zum Beispiel überein mit der Maximalzahl an Vergleichen, die zur Feststellung der An- oder Abwesenheit eines Schlüssels nötig sind.

Da auf einer Ebene eines Binärbaums höchstens doppelt so viele Knoten untergebracht werden können wie in der Ebene darüber und sich in der obersten Ebene genau 1 Knoten, die Wurzel, befindet, enthält ein Binärbaum der Höhe h höchstens 2h–1 Knoten. Ein solcher wird vollständiger Binärbaum (der Höhe h) genannt.

Andererseits enthält ein Binärbaum der Höhe h mindestens h Knoten, dies beispielsweise in der linearen Liste. Da bei ihnen von der Binärbaumeigenschaft nicht viel übrig geblieben ist, bezeichnet man sie als degenerierten oder entarteten Binärbaum.

Die Anzahl der Knoten n wird also eingegrenzt durch die Ungleichung

die sich exakt in folgende Ungleichung

für h umrechnet. Die Höhe h = n an der Obergrenze entspricht dem sequentiellen Suchen, während die Untergrenze beim besten „binären Suchen“ erreicht wird. Der Unterschied zwischen den beiden kann dramatisch sein: Haben wir beispielsweise ein Telefonbuch mit n = 220–1 = 1'048'575 Einträgen, dann haben wir an der Obergrenze die Höhe h = 1048575, müssen also im Mittel (1048575+1)/2 = 524'288 Vergleiche durchführen. An der Untergrenze ist h = 20, das heißt wir kommen bei einem vollständigen Binärbaum nach maximal 20 Vergleichen zur selben Entscheidung.

Suchstrukturen, die auch im schlechtesten Fall eine logarithmische Höhe garantieren, gelten als „effizient“.

Unter die binären Suchbäume rechnet man nur Strukturen, die zusätzlich zum Suchen auch Einfügungen und Löschungen von Schlüsseln unterstützen – Strukturen, die dynamisch sind. Durch die Modifikationsoperationen kann ein schon erreichter effizienter Aufbau wieder zerstört werden, m. a. W.: es können lineare Listen entstehen. Wenn nun eine Datenstruktur nicht nur das Suchen, sondern auch die Modifikationsoperationen effizient unterstützen soll, muss sie bei jeder Operation der Entartung entgegenwirken und den Baum auf effiziente Weise stets an jedem einzelnen Knoten in der „Balance“ halten.

Zwischenbemerkung: Die sehr bekannte Suchstruktur Binäre Suche unterstützt effizientes Suchen durch wiederholtes Bilden des Mittelwertes zwischen zwei Indizes in einem indizierbaren Feld (Array). Sie ist die direkteste und programmtechnisch einfachste Realisierung des Prinzips Teile und herrsche – eine Art Intervallschachtelung. Die durch sie erreichte Effizienz beim Suchen entspricht dem vollständigen Binärbaum. Bei Einfügungen und Löschungen sind aber Datentransporte mit einem Aufwand proportional zur Länge des Feldes erforderlich – sie unterstützt die Dynamik also nicht effizient. Sie ist aber als ein Vorläufer der binären Suchbäume anzusehen.

Was die Dynamik anbetrifft, gehen die binären Suchbäume über das Analogon der mathematischen Funktion und des Wörterbuchs hinaus, welches man meist als statisch ansieht mit höchsten sehr trägen Änderungen. Anwendungen von dynamischen Suchbäumen, bei denen sich die Zuordnung zwischen Argument und Funktionswert mit der Zeit ändert und bei denen die exakte augenblickliche Zuordnung wesentlich ist, haben meist Sachverhalte in der Informatik selbst zum Gegenstand. Ein Beispiel ist die Menge der Symbole (Variablen) in einem Programm, das von einem Interpreter auszuführen ist: der Interpreter muss jederzeit in der Lage sein zu entscheiden, ob einer Programmvariablen momentan ein Wert zugewiesen ist und ggf. welcher. Ein anderes Beispiel findet sich im Abschnitt #Beispiel einer Anwendung mit mehreren AVL-Zugriffspfaden.

Beim AVL-Baum wird die Balance über die Höhe definiert, er ist ein höhen-balancierter binärer Suchbaum. Weitere Lösungen zur Problematik der Entartung bei dynamischen Binärbäumen finden sich im

Definition

Als den Balance-Faktor[3] BF(t) eines Knotens resp. Teilbaums[Anm 1] t in einem Binärbaum bezeichnet man die Höhendifferenz

,

wobei H(t) die Höhe des Baums t, und tl der linke, tr der rechte Kindbaum von t ist.

Ein binärer Suchbaum ist genau dann ein AVL-Baum, wenn es Vorkehrungen gibt, die das Einhalten der AVL-Bedingung

an allen seinen Knoten t sicherstellen.

Diese Definition beschränkt die Höhe H(t) eines AVL-Baums t mit n Knoten durch die Ungleichung[4]

mit c := 1log2 Φ ≈ 1,440420, b := c2 log2 5 – 2 ≈ –0,327724 und Φ := 1+52 ≈ 1,618034 (Zahl des goldenen Schnitts).

Die untere Schranke kommt vom vollständig balancierten Binärbaum (bei dem bis auf die unterste Ebene alle Ebenen komplett sind); die obere vom Fibonacci-Baum, der bei gegebener Höhe einen AVL-Baum mit kleinster Knotenanzahl darstellt, also am schlechtesten balanciert ist.[Anm 2]

Operationen

Die wichtigsten Operationen bei den Suchbäumen – und damit beim AVL-Baum – sind: Suchen einerseits, sowie Einfügen und Löschen andererseits. Die Navigationsoperationen, das sind die verschiedenen Suchoperationen, das Traversieren, Aufsuchen erstes oder letztes Element und ähnliche, lassen den Baum unverändert und funktionieren im Prinzip auf jedem binären Suchbaum.

Mit der Eigenschaft, dass alle genannten Operationen im schlechtesten Fall (Worst Case) logarithmische Komplexität haben, gehört der AVL-Baum zu den höhenbalancierten binären Suchbäumen.

Suchen

Das Suchen (englisch: find, search oder locate) eines Elements anhand seines Schlüssels ist die wichtigste unter den Navigationsoperationen. Die Höhen-Balancierung des AVL-Baums versucht, auf diese Operation hin zu optimieren. Sie ermöglicht einen sogenannten direkten Zugriff (im Gegensatz zum sequentiellen Zugriff der Traversierung). Sie wird in der Regel als vorausgehende Operation sowohl beim Einfügen als auch beim Löschen eingesetzt.

Der binäre Suchbaum ist jederzeit so sortiert, dass links von jedem Knoten nur Knoten mit kleineren (oder gleichen) Schlüsseln, und rechts nur Knoten mit größeren (oder gleichen) Schlüsseln gespeichert sind. Damit das funktioniert, muss die vom Benutzer zur Verfügung gestellte Vergleichsfunktion eine Totalordnung (genauer: eine totale Quasiordnung) etablieren. In der Vergleichsfunktion steckt auch die Spezifikation des „Schlüssels“. Man findet also einen Schlüssel, indem man von der Wurzel des Baums ebenenweise nach unten wandert, und dabei nach links verzweigt, falls der gesuchte Schlüssel kleiner als der des aktuellen Knotens ist, oder nach rechts, wenn er größer ist. Stimmt ein Schlüssel überein, dann ist die Suche erfolgreich; erreicht man ein (Halb-)Blatt (das ist ein Knoten mit Ausgangsgrad 0 oder 1), dessen Schlüssel nicht übereinstimmt und dem in der geforderten Richtung ein Nachfolger fehlt, ist garantiert, dass der Schlüssel im Baum nicht existiert (siehe Suchen duplikatfrei). In diesem Fall heißt die Suche nicht erfolgreich. Das Ergebnis ist der zuletzt verglichene Knoten zusammen mit der zuletzt untersuchten Richtung, welche beide dem Anwender zurückgegeben werden, so dass er sie nach Bedarf als Einfügepunkt verwenden kann.

Sowohl duplikatfreie als auch Bäume mit Duplikaten (Elemente mit gleichen Schlüsseln) lassen sich implementieren. (Das ist weniger eine Frage der Vergleichsfunktion als eine der Anwendung.) Bei letzteren ist eine Suchoperation angebracht, die stets bis zu den (Halb-)Blättern hinab sucht und ermöglicht, ein Element gezielt je nach Anwendungssituation entweder links vom linkesten (am weitesten links liegenden) oder rechts vom rechtesten Duplikat einzufügen. Mit einer solchen Suchoperation kann die existierende Reihenfolge von Elementen mit gleichem Schlüssel bewahrt (oder eben auch umgekehrt) werden. Ist sie beispielsweise zusammen mit der Einfügeoperation Teil eines Sortieralgorithmus, dann wird dieser „stabil“ (siehe Stabilität (Sortierverfahren) mit erklärenden Beispielen). Zur Implementierung siehe Suchen mit Duplikaten.

Bei allen Varianten ist die Komplexität sowohl im Mittel als auch im schlechtesten Fall logarithmisch. Das Sortieren einer Masse von n Elementen durch wiederholtes Suchen und Einfügen kostet n-log-n, also nicht mehr als mit besten Sortierverfahren.

Traversieren (Iterieren, Enumerieren)

Die In-order-Traversierung, das heißt das Navigieren sowohl nach links wie nach rechts zum nächsten Nachbarn in der gegebenen Sortierfolge, ist besonders nützlich, wenn sie als Einzel-Operation, als Querschritt, vorliegt. So kann der Anwender eine Schleife in gewohnter Manier aufsetzen: Start mit der Suchoperation (oder dem ersten Element), Iteration mit der Traversieroperation.

Der Querschritt hat im Mittel konstanten und im schlechtesten Fall logarithmischen Aufwand. Bei einer Traversierung über den ganzen Baum wird jede Kante des Baums genau einmal absteigend und einmal aufsteigend besucht.

Aufsuchen erstes oder letztes Element

Verwandt hiermit ist die Positionierung auf das kleinste oder größte Element im Baum, welche sich sowohl im Mittel wie im schlechtesten Fall logarithmisch verhält.

Ein Intervallbegriff[5] (sowohl eigentlich wie uneigentlich) über der total geordneten Menge der Elemente im Baum lässt sich realisieren als auch eine Art „unscharfe“ Suche, nämlich nach „größer“ oder „kleiner“ (siehe binärer Suchbaum (Proximitäts-Suche)) – ebenfalls mit logarithmischem Aufwand.

Einfügen (Eintragen)

Es sei angenommen, dass die Navigation zum Einfügepunkt bereits erfolgt ist. Ein Einfügepunkt ist ein externer Knoten und ist ganz links oder ganz rechts oder zwischen 2 internen (und Schlüssel tragenden) Knoten, kann also auf jeden Fall durch einen (internen) Knoten zusammen mit einer Richtung (links oder rechts) spezifiziert werden. Die Knoten ganz links oder ganz rechts sind immer (Halb-)Blätter, wie auch von 2 Nachbarknoten immer genau einer ein (Halb-)Blatt ist. Ein solcher Knoten sei – zusammen mit der entsprechenden Richtung – als der unmittelbare Einfügepunkt bezeichnet. So wird er auch von einer nicht erfolgreichen Suchoperation geliefert.

Zur Einfügung (englisch: insert oder add) wird der Knoten mit dem neuen Schlüssel als Blatt am Einfügepunkt eingehängt – mit anderen Worten: das externe Blatt wird zum internen Blatt. Die Höhe des aus diesem Blatt bestehenden Teilbaums erhöht sich von 0 auf 1. Beim Aufstieg zum Elterknoten gibt es entsprechend der 3 Werte des ursprünglichen Balance-Faktors des Elterknotens 3 Möglichkeiten für den neuen (temporären) Balance-Faktor:

  1. Wird der Balance-Faktor zu 0, dann kommt man von einem Kindbaum, der vorher niedriger war, und die Höhe des betrachteten Knotens ändert sich nicht – mit der Folge, dass alle Balance-Faktoren weiter oben bleiben können, wie sie sind.
  2. Wird der Balance-Faktor zu ±1 (er muss vorher 0 gewesen sein), erhöht sich die Höhe des Teilbaums um 1 – die Überprüfung der Balance-Faktoren weiter oben muss weitergehen.
  3. Wird auf einer Ebene der Balance-Faktor zu ±2, muss der Teilbaum rebalanciert (siehe unten) werden. Danach hat bei einer Einfügeoperation der Teilbaum die gleiche Höhe wie vorher – mit der Folge, dass alle Balance-Faktoren weiter oben bleiben können, wie sie sind.

Das Einfügen des (n+1)-ten Knotens in einen AVL-Baum mit n Knoten hat im schlechtesten Fall logarithmischen Aufwand, beispielsweise wenn jede Ebene bis hinauf zur Wurzel überprüft werden muss. Da aber hierfür die Wahrscheinlichkeit von Ebene zu Ebene nach oben hin exponentiell abnimmt, ist der Bedarf für das Einfügen im Mittel konstant.[Anm 3] Hierbei ist das Aufsuchen der Einfügeposition nicht mitgerechnet.

Löschen (Austragen, Entfernen)

Die Navigation zum zu löschenden Knoten kann mittels einer Suche, aber auch durch einen Querschritt erfolgen. Beim Löschen (englisch: delete oder remove) sind mehr Fälle zu unterscheiden als beim Einfügen (siehe auch Binärbaum). Einfach sind die Fälle, wo der zu löschende Knoten ein (Halb-)Blatt ist. Hat er aber zwei Kinder, dann müssen die beiden frei werdenden Teilbäume neu aufgehängt werden. Dazu wählt man einen der In-order-Nachbarn, also entweder den rechtesten Knoten des linken Teilbaums oder den linkesten des rechten Teilbaums (von den beiden Teilbäumen kann man den eventuell höheren bevorzugen), um die beiden Teilbäume daran wieder aufzuhängen. Der hierfür erforderliche Abstieg geht maximal über so viele Stufen, wie die Höhe beträgt, und im Mittel über genau eine. Der Ersatzknoten wird in der Hierarchie des Baums an der Stelle des gelöschten Knotens eingeklinkt, muss dabei aber selbst aus seinem Herkunftsteilbaum entfernt werden – das ist einfach, da er ein (Halb-)Blatt ist (siehe Binärbaum).

Da letztlich in jedem Fall ein internes Blatt als Kindknoten entfernt wird, vermindert sich dessen Höhe von 1 auf 0. Beim Aufstieg zum Elterknoten gibt es entsprechend der 3 Werte des ursprünglichen Balance-Faktors des Elterknotens 3 Möglichkeiten für den neuen (temporären) Balance-Faktor:

  1. Wird der Balance-Faktor eines Knotens zu ±1 (er war vorher 0), dann ändert sich seine Höhe nicht – mit der Folge, dass alle Balance-Faktoren weiter oben bleiben können, wie sie sind.
  2. Wird ein Balance-Faktor zu 0, hat sich die Höhe des Teilbaums um 1 verringert – die Überprüfung der Balance-Faktoren weiter oben muss weitergehen.
  3. Wird ein Balance-Faktor zu ±2, muss der Teilbaum rebalanciert (siehe unten) werden. Danach kann bei der Löschoperation der neue Teilbaum eine um 1 niedrigere Höhe als haben vorher – mit der Folge, dass weiterhin überprüft und eventuell rebalanciert werden muss.
    Es kommt es aber auch vor, dass die gleiche Höhe wie vor dem Löschen herauskommt – so dass alle Balance-Faktoren weiter oben bleiben können, wie sie sind.

Der Aufwand fürs Löschen ist im schlechtesten Fall logarithmisch (mit oder ohne Suchen); im Mittel aber, wenn man das Auffinden des Löschkandidaten nicht mitrechnet, ist er konstant, da die Wahrscheinlichkeit für die Notwendigkeit, die Balance auf der nächsthöheren Ebene überprüfen zu müssen, nach oben hin exponentiell abnimmt.[Anm 4]

Rebalancierung

Wenn bei einer Operation die AVL-Balance verloren geht, das heißt ein Höhenunterschied von mehr als 1 zwischen zwei Geschwister-Teilbäumen entsteht, muss eine Rebalancierung durchgeführt werden. Dies muss innerhalb der aufgerufenen Funktion „automatisch“ (daher die Charakterisierung als self-balancing tree im Englischen) geschehen. Geeignete Reparaturwerkzeuge sind die sogenannten Rotationen.

Für Einfügungen und Löschungen, bei denen die temporäre Höhendifferenz nie über 2 hinausgeht, werden zwei Varianten benötigt: Einfach- und Doppelrotation. Eine Einfachrotation leistet die Rebalancierung, wenn das innere Kind des um 2 höheren Geschwisters (Z in den zwei Abbildungen 2 und 3), das ist das Kind mit einer Kindesrichtung, die der von Z entgegengesetzt ist, (t23 in der Abbildung 2 beziehungsweise Y in der Abbildung 3) nicht höher ist als sein Geschwister, das äußere Kind (t4 in beiden Abbildungen). Dieser Fall wird in der Literatur als Rechts-Rechts- (resp. gespiegelt Links-Links-)Situation bezeichnet, da X rechts- und Z nicht linkslastig ist, das heißt die zwei Balance-Faktoren 2 und 1 (beim Löschen auch 0) sind, kurz: die Balance zweimal hintereinander die gleiche Richtung hat. Der andere Fall, bei dem das innere Kind höher ist, wird von einer Doppelrotation abgedeckt – in der Literatur Rechts-Links- (resp. Links-Rechts-)Situation genannt, da X rechts- und Z linkslastig ist, das heißt die zwei Balance-Faktoren 2 und –1 sind, kurz: die Balance die Richtung wechselt.

Abb. 2: Einfachrotation Links(X)

Das Prinzip der Rotation in Binärbäumen sei anhand der Abbildung 2 erläutert. Eine einzelne Rotation lässt sich durch die Rotationsrichtung Links oder Rechts und durch die Wurzel des betroffenen Teilbaums spezifizieren, zum Beispiel Links(X). Sie bewirkt die Absenkung dieses X und die Anhebung eines anderen Knotens (hier: Z). Insgesamt geht es um die folgenden 3 Aktionen:

unten: Einhängen des zwischen Z und seinem Elter X befindlichen Teilbaums (hier: des linken Kindes t23 von Z) als (hier: rechtes) Kind bei X.[Anm 5]
Wippe: Einhängen von X als (hier: linkes) Kind bei Z.
oben: Feststellen des Elters (er sei E genannt) und der Kindesrichtung von X bei E. Einhängen von Z bei E als Kind dieser Richtung.[Anm 6]
Balkenwippe mit Kindern

Kurz: Eine Rotation „wippt“ eine Kante (hier: XZ) des Baums in ihre andere Orientierung (ZX). Dazu kommen die notwendigen Anpassungen an den beiden inzidenten Knoten (hier: X und Z).

Die Schlüssel bewegen sich bei diesen „Rotationen“ nur vertikal (innerhalb der senkrechten Raster). Die In-order-Reihenfolge der Knoten, die ja die Sortierreihenfolge abbildet, bleibt also vollkommen erhalten.

Eine Rotation umfasst nur eine konstante Anzahl von Verknüpfungsänderungen an einer konstanten Anzahl von Knoten.

Einfachrotation im AVL-Baum

Sie wird im Original Малое вращение kleine Drehung genannt.

Die obenstehende Abbildung 2 zeigt eine Rechts-Rechts-Situation. In der oberen Hälfte haben die beiden Teilbäume Z und t1 des Knotens X einen Höhenunterschied von 2. Das innere Kind t23 des um 2 höheren Knotens Z ist nicht höher als sein Geschwister t4.
Dies kann nach einem Einfügen in den Teilbaum t4 oder nach einem Löschen aus dem Teilbaum t1 auftreten. Der in der Abbildung 2 blass gehaltene Fall, dass t23 gleich hoch ist wie t4, kommt nur beim Löschen vor.

Die Rebalancierung (gezeigt in der unteren Hälfte der Abbildung) gelingt durch eine Einfachrotation nach links. Sie ist bereits oben als einzelne Rotation beschrieben. Die anzupassenden 3 Verknüpfungen sind in der Abbildung verstärkt gezeichnet.

Die Höhe des neuen Teilbaums Z ist bei einer Einfügung gleich der von X vor der Operation, während sie bei einer Löschung um 1 vermindert oder unverändert ist, je nachdem ob Z rechtslastig war oder nicht.

Die gespiegelte, die Links-Links-Situation wird von einer Einfachrotation nach rechts behandelt.

Abb. 3: Doppelrotation RechtsLinks(Z, X)
= Rechts(Z) dann Links(X)

Doppelrotation im AVL-Baum

Sie wird im Original Большое вращение große Drehung genannt.

Die oben in der Abbildung 3 gezeigte Situation unterscheidet sich von der in Abbildung 2 darin, dass der mittlere Teilbaum (mit Wurzel Y), das ist das innere Kind des um 2 höheren Knotens Z, höher ist als sein Geschwister t4 – eine Rechts-Links-Situation, da X rechts- und Z linkslastig ist.
Das kann nach einer Einfügung in einen der Teilbäume t2 oder t3 oder nach einer Löschung aus dem Teilbaum t1 passieren. Der in der Abbildung 3 in Standardfarbe gehaltene Fall, bei dem t2 gleich hoch ist wie t3, kommt nur beim Löschen vor, während die 2 höhenungleichen Fälle (genau ein Teilbaum blass) sowohl beim Einfügen wie beim Löschen vorkommen. Der vierte Fall (zweimal blass) bedarf keiner Rebalancierung.

Die Doppelrotation, deren Ergebnis im unteren Drittel der Abb. gezeigt ist, ist eine Rechtsrotation durch Z (gegen die Linkslastigkeit von Z, mittleres Drittel) gefolgt von einer Linksrotation durch X (gegen die Rechtslastigkeit von X). Sie bewirkt eine zweimalige Anhebung des höheren (und inneren) Kindes Y von Z.

Die anzupassenden 5 Verknüpfungen sind in der Abbildung verstärkt gezeichnet.

Die Höhe des neuen Teilbaums Y ist nach einer Einfügung gleich wie die von X vor der Operation, während sie nach einer Löschung um 1 vermindert ist.

Bei einer Links-Rechts-Situation wird die gespiegelte Version, das heißt eine Linksrotation gefolgt von einer Rechtsrotation, benötigt.

Beispiel

Der AVL-Baum in der Abbildung 1 wird

  • nach der Löschung des Knotens G durch die zwei Einfachrotationen Rechts(F), später Links(J)
  • nach der Einfügung eines Knotens T durch die Doppelrotation RechtsLinks(VP)

rebalanciert.

Operationen mit ganzen AVL-Bäumen

Verketten

Abb. 4: Verkettung von 2 AVL-Bäumen

Zwei AVL-Bäume können mit logarithmischem Aufwand verkettet (konkateniert) werden (englisch: concat oder auch nur cat). Für die Sortierfolge müssen natürlich alle Schlüssel des ersten Baums allen Schlüsseln des zweiten vorangehen.[Anm 7]

Spalten

Abb. 5: Ein Indukti-
onsschritt beim Spalten eines AVL-Baums

Etwas komplizierter als das Verketten ist das Spalten (englisch: split) eines AVL-Baums in zwei separate AVL-Bäume an einem externen Knoten, also einer Stelle zwischen zwei internen Knoten (Schlüsseln), die als Paar (Knoten, Richtung) einschließlich des Pfades zur Wurzel gegeben sei. Links davon liegt die linke Partition mit den kleineren Schlüsseln und rechts davon die rechte mit den größeren. Die so spezifizierte Trennlinie (rot in der Abbildung 5) zerschneidet Kanten des gegebenen Baums auf dem Pfad des Knotens zur Wurzel, so dass sich links wie rechts ein Wald von „Schnipseln“ ergibt. Schließlich kann jeder der beiden Wälder mit logarithmischem Aufwand zu einem AVL-Baum zusammengefügt werden.[Anm 8][6]

Anwendungsbeispiele

Die Massenlöschung von allen Schlüsseln in einem Intervall kann durch zweimaliges Spalten und einmaliges Verketten geschehen oder, wenn das kleinste oder größte Element mit dabei ist, durch einmaliges Spalten.

Eine Masseneinfügung kann durch einmaliges Spalten und zweimaliges Verketten durchgeführt werden, sofern die Menge als AVL-Baum vorbereitet ist und ihre Schlüssel in einem Intervall liegen, das im Zielbaum nicht vorkommt.

Implementierung

Zusätzlich zum Bedarf des binären Suchbaums muss in einem Knoten der Balance-Faktor mit seinen 3 Werten untergebracht werden: macht 2 Bits.[Anm 9]

Kopf

Da die Wurzel einer Löschung oder einer Rotation anheim fallen, somit den Baum nicht repräsentieren kann, muss diese Rolle von einer anderen Datenstruktur übernommen werden, die in der Literatur Kopf[7] genannt wird. Sie enthält den Zeiger zur Wurzel und fungiert als eine Art Elter der Wurzel.

Iterative Programmierung

In der Literatur werden die Operationen häufig in rekursiver Programmierung vorgestellt. Der Anwender hat aber mehrere Vorteile davon, wenn der Implementierer die vom Aufschreiben her eleganten Rekursionen durch simple Iterationen ersetzt hat, da dadurch Prozeduraufrufe und -rücksprünge eingespart werden und der Speicherbedarf für den Programm-Stapelspeicher konstant gehalten wird. Es geht aber nicht nur um Ressourcenschonung. Bei der Traversieroperation wird dadurch beispielsweise die Programmierung der Anwendung wesentlich vereinfacht.[8] Für das Aufbewahren des Rückwegs zu Wurzel und Kopf, der bei Modifikationen zur Anpassung der Balance-Faktoren, aber auch beim Traversieren gebraucht wird und der bei der rekursiven Programmierung neben anderem im Programmstapelspeicher steckt, muss dann ein anderes, explizites Konstrukt gewählt werden, welches sich im Cursor (siehe unten) subsumieren lässt. Dadurch wird eine Trennung der modifizierenden Operationen von der Navigation möglich.

Trennung der navigierenden von den modifizierenden Operationen

Einfüge- und Löschoperation sind sinnvollerweise von der Suchoperation zu trennen, wenn zum Einfügepunkt beziehungsweise Knoten auch auf andere Weise als mit der Standardsuchoperation navigiert werden soll, beispielsweise mittels eines Querschritts oder mithilfe einer zweiten Suchstruktur für dasselbe Element wie im #Beispiel einer Anwendung mit mehreren AVL-Zugriffspfaden.

Diese Modularisierung der Navigations- von den modifizierenden Operationen setzt den im Mittel unterlogarithmischen (sprich: konstanten) Aufwand der letzteren frei, denn ein Aufstieg bis zur Wurzel ist (wie in [Anm 3] und [Anm 4] gezeigt) nur in Ausnahmefällen erforderlich. In Anwendungen mit starkem sequentiellem Anteil kann sich das positiv auf die Laufzeit auswirken.

Cursor

Beim Suchen wird ein Paar (Knoten, Richtung) erzeugt, welches geeignet ist, beim Einfügen den Einfügepunkt zu spezifizieren. Beim Löschen wird der zu löschende Knoten durch die Komponente Knoten bezeichnet, und die Komponente Richtung spezifiziert, wohin der Cursor nach der Löschung fortschreiten soll. Beim Traversieren gibt Knoten den Ausgangspunkt und Richtung die gewünschte Richtung der Navigation an, um im Ergebnis wieder bei einem solchen Paar anzukommen. Damit erzeugen und/oder verbrauchen alle wichtigen Operationen ein Konstrukt, das (in Analogie zum Beispiel zu den Datenbanken) Cursor genannt wird.[9]

Die Größe des Cursors hängt entscheidend davon ab, ob die AVL-Knoten einen Zeiger zum Elter enthalten oder nicht.

  1. Elterzeiger vorhanden: Ein Paar (Knoten, Richtung) stellt einen vollwertigen Cursor dar.
    Ein Cursor wird nach einer (den Cursor nicht pflegenden) Operation dann und nur dann ungültig, wenn es sich um eine Löschung des Knotens dieses Cursors handelt.
    Mit dem prozentualen Aufschlag auf den Speicherbedarf für die Datenstruktur erkauft man sich auf jeden Fall eine prozentuale Einsparung an Laufzeit, da der Rückweg zu Wurzel und Kopf immer schon gesichert ist.
  2. Zeiger zum Elterknoten nicht vorhanden („Cursor mit Stapel“): Zusätzlich zum Paar (Knoten, Richtung) muss der Pfad vom Knoten zu Wurzel und Kopf im Cursor gehalten werden. Die Länge des Cursors entspricht damit der maximalen Höhe des Baums.[Anm 10]
    Bei allen Operationen ist der Zugriff zum Elterknoten über den Stapel im Cursor geringfügig teurer als über den Elterzeiger. Soll der Pfad im Cursor auch nach einer modifizierenden Operation gültig gehalten werden (beispielsweise für sequentielle Einfügungen oder Löschungen), kommt noch ein zusätzlicher prozentualer (im Mittel konstanter und im schlechtesten Fall logarithmischer) Aufschlag hinzu. Dies kann so aber nur für einen Cursor, den Eingabecursor, erbracht werden.

Benötigt eine Anwendung mehrere Cursor für ein und denselben Suchbaum und über Änderungen an ihm hinweg, dann kann das Aufrechterhalten der Konsistenz der Cursor mit Stapel (zum Beispiel durch erneutes Suchen mit im Mittel logarithmischen Kosten) so aufwändig werden, dass es wirtschaftlicher ist, dem Baum Elterzeiger zu spendieren.

Beispiel einer Anwendung mit mehreren AVL-Zugriffspfaden

Als Beispiel für eine Anwendung mit 2 Zugriffspfaden sei ein klassisches Speichermanagement gebracht. Elemente der Datenstruktur sind die freien Speicherblöcke mit den Attributen (Feldern) Größe und Ort. Für jedes der beiden Felder gebe es eine Suchstruktur. Beim Akquirieren wird ein Block von Mindestgröße gesucht, ausgetragen und ein eventueller Rest wieder eingetragen. Bei der Speicherrückgabe wird nach Ort gesucht, auf Konfliktfreiheit mit den Nachbarblöcken geprüft (ebenfalls ein Beispiel für die Nützlichkeit der Querschritts) und der zurückzugebende Block gegebenenfalls mit diesen verschmolzen. Alle Veränderungen müssen auf beiden Suchstrukturen durchgezogen werden. Sind Elterzeiger vorhanden, dann erspart das Hangeln von der einen Suchstruktur zur anderen einen Suchvorgang.

Parallelisierung

Wird iterativ programmiert, dann kann die Überprüfungs- und Reparaturschleife auch in der umgekehrten Richtung, das heißt von Kopf und Wurzel zum Blatt, durchlaufen werden.[10] Das ist insbesondere dann interessant, wenn auf den Baum hochgradig parallel (konkurrent) zugegriffen werden soll. Zum Beispiel würde in einem Szenario „Suchen und Einfügen“ die Suchoperation sich den tiefsten (letzten) unausgewogenen Knoten auf dem Pfad merken, ab dort den Baum sperren[Anm 11] und die Vergleichsergebnisse aufbewahren. Zum Fertigstellen der Einfügeoperation müsste dann der gesperrte Vorfahr (gegebenenfalls nach einer Rebalancierung) auf ausgewogen und alle seine Nachfahren bis zum Blatt auf die den eingeschlagenen Richtungen entsprechenden Balance-Faktoren gesetzt werden. Der Vorteil wäre, dass alle Knoten außerhalb des gesperrten Teilbaums von den anderen Prozessorkernen parallel zur laufenden Einfügung konsistent besichtigt und auch verändert werden könnten.

Vergleich mit Rot-Schwarz-Baum

Kein AVL-Baum

Die Menge der AVL-Bäume ist eine echte Teilmenge in der Menge der Rot-Schwarz-Bäume. Denn jeder Binärbaum, der in AVL-Form ist, lässt sich in einer das Rot-Schwarz-Kriterium erfüllenden Weise einfärben.[Anm 12] Es gibt aber Rot-Schwarz-Bäume, die nicht AVL-balanciert sind. Die nebenstehende Abb. zeigt zum Beispiel einen Rot-Schwarz-Baum mit 6 (inneren) Knoten und der externen Pfadlängensumme 21, während 20 die größte externe Pfadlängensumme bei AVL-Bäumen (und zugleich die kleinstmögliche für alle Binärbäume) dieser Größe ist. Konsequenterweise ist auch die Worst-Case-Höhe des AVL-Baums kleiner als die des Rot-Schwarz-Baums, und zwar um den Faktor c20,720. Allgemein wird das Suchzeitverhalten des AVL-Baums als günstiger angesehen.

Der Speicherplatzbedarf ist praktisch identisch: 1 Bit für die Farbe gegenüber 2 oder auch 1 Bit(s)Referenzfehler: Ungültige <ref>-Verwendung: „ref“ ohne Inhalt muss einen Namen haben. für den Balance-Faktor.

Der Platzbedarf und das Laufzeitverhalten für die beschriebenen Operationen verhalten sich im Mittel und im Worst Case identisch. Zwar bietet der Rot-Schwarz-Baum amortisiert konstante Modifikationskosten[11] und der AVL-Baum nur im Mittel konstante.[Anm 13] Anwendungen von binären Suchbäumen, die nur Sequenzen von Einfügungen und Löschungen – aber keine intervenierenden Suchoperationen – enthalten, nutzen den Zweck des Suchbaums nicht aus. Sieht man also von dieser Art Anwendungen ab, ist das Gesamtverhalten einer Mischung von Suchen und Modifikation bei beiden Datenstrukturen amortisiert, im Mittel und im Worst Case logarithmisch.

Realistische Anwendungssituationen mit Performancedaten und -vergleichen – auch mit weiteren Suchalgorithmen und Spielarten der Datenstrukturen – finden sich bei Ben Pfaff[12]. Seine Ergebnisse zeigen in 79 Messungen unter anderem die sehr große Ähnlichkeit von AVL-Bäumen (AVL) und Rot-Schwarz-Bäumen (RB) mit Laufzeitverhältnissen AVLRB zwischen 0,677 und 1,077 bei einem Median von ≈0,947 und einem geometrischen Mittelwert von ≈0,910.

Anwendungen

Wie Ben Pfaff[12] zeigt, decken die dynamischen Suchbaumstrukturen AVL-Baum, Rot-Schwarz-Baum und Splay-Baum dieselben wesentlichen Funktionen ab. Große Unterschiede stellt er im Laufzeitverhalten fest, wobei der AVL-Baum in Median und Mittelwert am besten abschneidet.

In der Informatik haben die dynamischen Suchbaumstrukturen einen großen Einsatzbereich als grundlegende Hilfsmittel bei:

In diesem Artikel sind erwähnt:

Bei Ben Pfaff[12] finden sich weitere (ähnliche) Anwendungen (alle unter x86-based Linux):

  • Verwaltung von Virtual Memory Areas (VMAs) unter Einschluss von range queries zur Feststellung des Überlappens von existierenden VMAs (S. 4).
  • Eindeutige Kennzeichnung von IP-Paketen (S. 7).

Historisches

Wie im Abschnitt #Motivation erwähnt, kann die sehr bekannte Suchstruktur Binäre Suche im Array als Vorläufer der dynamischen Suchbaumstrukturen gelten. Als naheliegende Umsetzung des Nachschlagens in einem (sortierten) Wörterbuch, dürfte sie mehrfach ohne Kenntnis anderer Implementierungen entwickelt und implementiert worden sein. Im dynamischen Anwendungsfall kann sie aber mit den neueren Entwicklungen nicht mithalten, obwohl sie im statischen Fall eine hervorragende Lösung ist. Es gibt Makros, die einen Compiler veranlassen, zu einer gegebenen Tabelle von (Argument, Werte)-Paaren Quelltext ohne Programmschleife für eine binäre Suche zu erzeugen.

Im Jahr 1962 erschien zum ersten Mal eine dynamische Suchbaumstruktur in Form des AVL-Baums. Seine Erfinder sind die genannten sowjetischen Mathematiker Georgi Adelson-Velski und Jewgeni Landis. Ihr Beitrag im Journal Doklady Akademii Nauk SSSR wurde schon im gleichen Jahr ins Englische übersetzt. Die Übersetzung trägt (wie entsprechend das Original) den sehr ehrgeizigen Titel „An algorithm for the organization of information“. Der Name AVL-Baum findet sich in dieser Übersetzung noch nicht.

Im Jahr 1970 veröffentlichte Rudolf Bayer[13] seine erste Arbeit über den B-Baum. Er ist kein Binärbaum und unterstützt heterogene Speicher, beispielsweise Hauptspeicher und Hintergrundspeicher, und wird bei Datenbanksystemen eingesetzt.

Danach folgte im Jahr 1972 zunächst unter dem Namen „symmetric binary B-tree“ der Rot-Schwarz-Baum von Rudolf Bayer[14]. Ihm war die Balance-Regel des AVL-Baums zu streng. Eine Umbenennung erfolgte 1978 von Leonidas Guibas und Robert Sedgewick[15] in das heute übliche „red–black tree“, später auch „RB tree“.

Splay-Bäume wurden im Jahr 1985 von Daniel Sleator und Robert Tarjan[16] unter dem Namen „Self-Adjusting Binary Search Trees“ vorgestellt. Sie sind noch dynamischer als die vorgenannten, indem sie sich auch bei Suchoperationen verändern.

Eine grobe Gegenüberstellung dynamischer Suchbäume findet sich im

Weiterführende Informationen

Siehe auch

Literatur

| Donald E. Knuth]]: The Art of Computer Programming, Volume 3, Sorting and Searching, Second Edition. Addison-Wesley, 1998, ISBN 0-201-89685-0, 6.2.3 Balanced Trees, S. 458–478.

Commons: AVL-Bäume – Sammlung von Bildern, Videos und Audiodateien

Einzelnachweise

  1. Thomas H. Cormen u.a.: Introduction to Algorithms. 2. Auflage, MIT Press, Cambridge (Massachusetts) 2001, S. 296.
  2. G. M. Adelson-Velsky, E. M. Landis: Один алгоритм организации информации. In: Doklady Akademii Nauk SSSR. 146. Jahrgang, 1962, S. 263–266. (russisch)
    Englische Übersetzung von Myron J. Ricci: An algorithm for the organization of information. In: Soviet Mathematics 3, 1962, S. 1259–1263.
  3. In der englischen Literatur meist „balance factor“, so bei Donald E. Knuth: The Art of Computer Programming, Band 3, Sorting and Searching, 2. Auflage, Addison-Wesley, 1998, S. 459; „Höhenbalance“ bei Kurt Mehlhorn: Datenstrukturen und effiziente Algorithmen. Teubner, Stuttgart 1988, S. 295.
  4. Donald E. Knuth: The Art of Computer Programming, Band 3, Sorting and Searching, 2. Auflage, Addison-Wesley, 1998, S. 460.
  5. „Range queries“ bei Kurt Mehlhorn, Peter Sanders: Algorithms and Data Structures. The Basic Toolbox. Springer, Berlin / Heidelberg 2008, ISBN 978-3-540-77977-3, doi:10.1007/978-3-540-77978-0. S. 156.
  6. Costin S, der in seinem Projekt AvlTree.cs AVL-Bäume mit Concat- und Splitoperationen implementiert, fordert dazu die Aufzeichnung der vollen absoluten Höhe in jedem Knoten. Man kann aber – wie gezeigt – mit den Balance-Faktoren auskommen, ohne den Aufwand zu erhöhen.
  7. „Header node“ und „HEAD“ bei Donald E. Knuth: The Art of Computer Programming, Band 3, Sorting and Searching, 2. Auflage, Addison-Wesley, 1998, S. 462; totum pro parte „tree data structure“ bei Ben Pfaff: An Introduction to Binary Search Trees and Balanced Trees. Free Software Foundation, Inc. Boston 2004.
  8. Siehe dazu Traversierung (mit Codebeispielen) und Ben Pfaff: An Introduction to Binary Search Trees and Balanced Trees. Free Software Foundation, Inc. Boston 2004, S. 47 „4.9.2 Traversal by Iteration“.
  9. Ben Pfaff: Performance Analysis of BSTs in System Software. Stanford University 2004, S. 3, gibt einem Objekt mit sehr ähnlicher Funktionalität den Namen „traverser“ und offeriert für Suchen, Einfügen und Löschen eine Standard- und eine Traverser-Variante.
  10. „Top-Down-Strategie“ bei Kurt Mehlhorn: Datenstrukturen und effiziente Algorithmen. Teubner, Stuttgart 1988, S. 197-198.
  11. Kurt Mehlhorn, Peter Sanders: Algorithms and Data Structures. The Basic Toolbox. Springer, Berlin / Heidelberg 2008, ISBN 978-3-540-77977-3, doi:10.1007/978-3-540-77978-0. S. 165.
  12. a b c Ben Pfaff: Performance Analysis of BSTs in System Software. Stanford University 2004.
  13. Rudolf Bayer, Edward M. McCreight: Organization and Maintenance of Large Ordered Indices. Mathematical and Information Sciences Report No. 20. Boeing Scientific Research Laboratories, 1970.
  14. Rudolf Bayer: Symmetric binary B-Trees: Data structure and maintenance algorithms. In: Acta Informatica. 1. Jahrgang, Nr. 4, 1972, S. 290–306, doi:10.1007/BF00289509 (springerlink.com).
  15. Leonidas J. Guibas, Robert Sedgewick: A Dichromatic Framework for Balanced Trees. 1978, S. 8–21, doi:10.1109/SFCS.1978.3 (ieeecomputersociety.org).
  16. Daniel D. Sleator, Robert Tarjan: Self-Adjusting Binary Search Trees (PDF; 6,1 MB). In: Journal of the ACM (Association for Computing Machinery). Band 32, Nr. 3, 1985, S. 652–686, doi:10.1145/3828.3835.

Anmerkungen

  1. Da einem Knoten in umkehrbar eindeutiger Weise der von ihm gewurzelte maximale Unterbaum, „sein“ Teilbaum, zugeordnet werden kann, sollen der Kürze halber Eigenschaften wie Höhe, Balance, Elter-Kind- und Geschwister-Beziehung usw. einem Knoten und einem solchen Teilbaum in gleicher Weise zukommen. Umgekehrt fungiert, zum Beispiel beim Umhängen eines Teilbaums, dessen Wurzel als „Henkel“.
  2. Ähnlich wie es bei einem unbalancierten binären Suchbaum eine hohe Wahrscheinlichkeit gibt, dass er bei zufälligen Einfügungen eine gewisse Balanciertheit erlangt (siehe auch Pfaff 2004b S. 2.), ist auch beim AVL-Baum die Wahrscheinlichkeit sehr hoch, dass er besser balanciert herauskommt als seine am schlechtesten balancierte Ausprägung, der Fibonacci-Baum. Strebt beim Fibonacci-Baum die Anzahl n der Knoten gegen unendlich, dann verhält sich seine mittlere Suchtiefe über alle Knoten asymptotisch wie a log2 n mit a := c Φ5 = Φ5 log2 Φ ≈ 1,042298, weicht also nur um 4 Prozent von der idealen eines vollständig balancierten Binärbaums ab. Wird über alle AVL-Bäume einer festen Höhe oder über alle Einfügereihenfolgen zu einer festen Knotenzahl gemittelt, liegt der Mehraufwand gegenüber den jeweils bestmöglichen, den vollständig balancierten Binärbäumen zwischen 0 und 2 Prozent (Zahlen für kleine Bäume im Abschnitt Mittlere Suchtiefe bei AVL-Bäumen).
  3. a b Sei G die Anzahl der Knoten, die nicht Blätter sind und die zwei Teilbäume gleicher Höhe haben, und U die Anzahl der Knoten mit ungleich hohen Teilbäumen, dann ist u := UG+U ∈ [0,1] der Anteil der unausgewogenen. Nur die Fibonacci-Bäume und die anderen dünnsten AVL-Bäume erreichen exakt u = 1. Dagegen kommen nur die vollständigen Binärbäume mit Knotenzahl 2h–1 auf exakt u = 0. Ferner sei angenommen, dass der gespiegelte Baum gleich häufig vorkommt wie das Original.
    Von folgender Situation sei rekursiv ausgegangen: Bei einer Einfügung habe sich ergeben, dass die Höhe des Teilbaums, in dem die Einfügung stattfand, um 1 zugenommen hat. Ferner sei angenommen, dass es sich um einen rechten Kindbaum handelt (dazu passen die Abbildungen 2 und 3; die gespiegelte Alternative linker Kindbaum sei dem Leser überlassen, sie führt zu denselben Werten). Bei der Überprüfung der Lage im Elterknoten dieses Teilbaums sind die folgenden Fälle zu unterscheiden:
    BF
    vor
    Einfügung
      Häufigkeit BF
    nach
    Einfügung
    Rebalan-
    cierung
    erforderlich
    BF
    da-
    nach
    Teilbaum
    wird
    höher um
    Nächste
    Ebene zu
    überprüfen
    –1 links höher u2 0 Nein 0 Nein
    0 ausgewogen 1–u 1 Nein 1 Ja
    1 rechts höher u2 2 Ja 0 0 Nein
    Tab. 2: Nach dem Einfügen: Die neuen Balance-Faktoren (BF) in Abhängigkeit von den alten

    Die Wahrscheinlichkeit, bei einer Einfügung ausgehend von einer Ebene die nächsthöhere überprüfen zu müssen, ist also q := 1–u. Die Wahrscheinlichkeit, dass wenigstens n Ebenen überprüft werden müssen, ist qn und, dass genau n Ebenen überprüft werden müssen, ist qnqn+1 = (1–q) qn. Somit summiert sich die Anzahl der zu überprüfenden Ebenen im Mittel auf

    was bedeutet, dass der Mittelwert für die Anzahl der zu überprüfenden Ebenen zwischen 1 und 3 liegt, wenn 0,5 ≥ u ≥ 0,25.

  4. a b Über den Anteil der unausgewogenen Knoten seien dieselben Annahmen wie bei der Einfügung gemacht.
    Folgende Situation sei rekursiv angenommen: Nach einer Löschung habe sich die Höhe des Teilbaums, in dem die Löschung stattfand, um 1 vermindert. Ferner handele es sich ohne Beschränkung der Allgemeinheit um einen linken Kindbaum. Bei der Überprüfung der Lage im Elterknoten dieses Teilbaums sind die folgenden Fälle zu unterscheiden:
    BF
    vor
    Löschung
      Häufigkeit BF
    nach
    Löschung
    Rebalan-
    cierung
    erforderlich
    BF
    da-
    nach
    Teilbaum
    wird nied-
    riger um
    Nächste
    Ebene zu
    überprüfen
    –1 links höher u2 0 Nein 1 Ja
    0 ausgewogen 1–u 1 Nein 0 Nein
    1 rechts höher u2 u2 (1–u) 2 Ja 1 1 Nein
    u2 (u2+u2) 0 2 Ja
    1 Die vorletzte Zeile bezieht sich auf den Fall der Einfachrotation mit gleich hohen Kindern und

    2 die letzte auf Rotationen mit nicht gleich hohen Kindern, das heißt Doppelrotation (linkes Kind höher)
      oder Einfachrotation mit dem rechten Kind höher.

    Tab. 3: Nach dem Löschen: Die neuen Balance-Faktoren (BF) in Abhängigkeit von den alten

    Bei einer Löschung ergibt sich eine Wahrscheinlichkeit von weniger als u für das Erfordernis, die nächsthöhere Ebene überprüfen zu müssen. Also summiert sich die Anzahl der zu überprüfenden Ebenen im Mittel auf weniger als u1–u, was bei u ≤ 0,5 bedeutet, dass im Mittel weniger als ungefähr eine Ebene eines Niveaus ≥ 2 überprüft werden muss.

  5. t23 kann leer sein, was dem Fall h = 0 in der Abbildung 2 entspricht.
  6. Ist Z die Wurzel, dann ist E der „Kopf“ (siehe den Abschnitt Kopf) und Z bei E als neue Wurzel einzuhängen.
  7. Algorithmus: Man steigt an der rechten Flanke des ersten Baums (siehe Abbildung 4. Die grauen Pfeile zeigen den Weg durch den Graphen.) und an der linken des zweiten bis zu den Blättern hinab und merkt sich die Knoten auf den Pfaden zusammen mit ihren Höhen.
    Ohne Beschränkung der Allgemeinheit sei der erste Baum der höhere (wie in der Abbildung). Dem zweiten Baum wird sein erstes Element H entnommen. Es spielt nachher die Rolle eines „Bindeglieds“. Die Höhe des zweiten Baums sei jetzt h. Man sucht nun in der rechten Flanke des ersten Baums nach einem Knoten G der Höhe h+1 oder h (einen von beiden muss es geben; gibt es beide, sei der höhere bevorzugt). Man macht den zweiten Baum zum Geschwister von G, wobei das Bindeglied H als Elter von beiden eingesetzt wird. Dann wird H beim Elter F von G an Gs Stelle als Kind eingehängt. Der maximale Höhenunterschied zwischen E und dem neuen H ist 2 (wenn E die Höhe h–1 hat, dann hat G die Höhe h, und das neue H die Höhe h+1). Nach einer ersten Anpassung mit eventueller (Doppel-)Rotation müssen noch aufsteigend die Balance-Faktoren wie bei einer Einfügung überprüft und gegebenenfalls korrigiert werden.
    Dabei kann die Höhe des Baums um 1 zunehmen.
  8. Die Schnipsel sind Bäume in AVL-Form bis auf eventuell einen Knoten („Stummel“) auf hoher Ebene mit Ausgangsgrad 1 (nicht unbedingt die Wurzel des Schnipsels), der beim Verketten mit dem nächstniedrigeren Schnipsel auf der gleichen Seite das Bindeglied spielt. Algorithmus (vollständige Induktion nach den Schnipseln, auszuführen auf beiden Seiten der Trennlinie): Gibt es ein Kind des gegebenen Knotens in der gegebenen Richtung, dann stellt der Teilbaum dieses Kindes einen Schnipsel dar, welcher schon in AVL-Form ist. Der gegebene Knoten liegt auf der Gegenseite und ist zum Stummel geworden. Er wird in den Teilbaum seines nunmehr „Einzelkindes“ je nach Seite als rechtestes beziehungsweise linkestes Element einfügt. Auf beiden Seiten ist der niedrigste Schnipsel jetzt ein AVL-Baum. Damit ist der Induktionsanfang auf beiden Seiten geschafft.
    Induktionsschritt (siehe Abbildung 5): Der niedrigste Schnipsel von beiden Seiten ist nach Induktionsannahme ein AVL-Baum. Ohne Beschränkung der Allgemeinheit sei er links und heiße I. Beim Aufstieg im Pfad gelangt man zum nächsthöheren ebenfalls linken Stummel H, dessen ursprüngliche Höhe mit h bezeichnet sei. Zerschnittene Kanten (gestrichelte schwarze Pfeile in der Abbildung) und die von ihnen zurück gelassenen Stummel erkennt man am Wechsel der Kindesrichtungen beim Aufstieg im Pfad. Die Höhen sind anhand der Balance-Faktoren aufs genaueste mitzuschreiben. Der Teilbaum des Einzelkindes D von H wird mit dem Schnipsel I unter Einsatz von H als Bindeglied verkettet. Dabei kann seine Höhe um 1 zunehmen. Darauf macht man D zum Kind seines vormaligen Großelters C in der Kindesrichtung seines vormaligen Elters H, das heißt zum Geschwister seines vormaligen Onkels B. Die Höhendifferenz zu letzterem ist maximal 3. Die lokale AVL-Balance lässt sich durch Rotationen herstellen, wobei die Höhe des Teilbaums um 1 abnehmen kann. (Die Vorgehensweise bei einem Höhenunterschied von 3 ähnelt der bei einem von 2: Ist das innere Kind des höheren Geschwisters niedriger als das äußere Kind, kann man die AVL-Balance mit einer Einfachrotation herstellen. Ist es höher, kommt man mit einer Doppelrotation durch. Sind beide Kinder gleich hoch, hängt es vom Balance-Faktor des inneren Kindes ab: ist der ausgewogen, macht’s eine Doppelrotation; ist er außenlastig, schaffen’s zwei Rotationen; ist er innenlastig, geht’s mit drei Rotationen.) Bei den Ebenen darüber sind die Balance-Faktoren wie bei einer Löschung zu überprüfen und gegebenenfalls anzupassen, wobei die Gesamthöhe wieder um 1 abnehmen kann. Damit ist I so in den neuen Schnipsel A integriert, dass A wieder ein AVL-Baum ist – und den Schnipsel I des nächsten Rekursionsschrittes darstellt.
    Die innerhalb eines Rekursionsschrittes besuchten Kanten (graue Pfeile in der Abbildung 5) betreffen nur Ebenen zwischen den Wurzeln von zwei Schnipseln, die auf derselben Seite übereinander liegen. Die Rekursionsschritte auf der linken und rechten Seite greifen reißverschlussartig ineinander: der Aufstieg auf der rechten wird auf der linken Seite beim Verketten ab- und wieder aufgestiegen, woran sich ein Aufstieg anschließt, der auf der rechten Seite ab- und wieder aufgestiegen wird, usw. Eine Ebene wird höchstens dreimal besucht, jedes Mal mit konstantem Aufwand. Damit ist der Gesamtaufwand fürs Spalten proportional zur Gesamthöhe.
  9. 1 Bit, wenn bei den Kindern aufgezeichnet. Wird dem Bit die Bedeutung „Höhensprung oberhalb“ gegeben, dann kann die Höhe im Pfad ohne Kenntnis der Kindesrichtung berechnet werden. Dennoch: 2 Balance-Bits nebeneinander sind für die Modifikationsoperationen effizienter.
  10. Als eine Datenstruktur, die im homogenen Arbeitsspeicher (Hauptspeicher) untergebracht ist, ist der AVL-Baum durch dessen Größe beschränkt, also auch die Höhe des Baums und die Längen der Pfade von Blatt zu Wurzel. Gängig sind Hauptspeicher mit 32 Bit und 64 Bit breiten 8-Bit-Byte-Adressen.
    Breite
    eines
    Zeigers
    in Bit
    maximale Anzahl adressierbarer Bytes minimale Knotenlänge
    (2 Zeiger+1 Byte
    +Nutzdaten1)
    maxi-
    male Anzahl Knoten
    Knotenzahl des nächstgrößeren Fibonacci-Baums … … der Höhe
    32 4294967296 2·4+1+3 = 12 3,6·108 433494436 41
    64 18446744073709551616 2·8+1+7 = 24 7,7·1018 1100087778366101930 86
    1 inklusive Schlüssel. Bei mehr Nutzdaten und weniger Hauptspeicher
      verringert sich die maximale Knotenzahl und Höhe entsprechend.
    Tab. 4: Maximale Höhe in Abhängigkeit von der Adressierungsbreite

    Fazit: Es ist bei AVL-Bäumen vertretbar, Cursor mit Stapeln bereitzustellen, die so groß sind, dass ein Überlauf nicht auftreten kann.

  11. Genau genommen bedarf es einer Kette mit den Gliedern Sperren(Kind) und Entsperren(Elter) bis zum ersten unausgewogenen Knoten, der zunächst gesperrt bleibt, bis er in ein neues Wechselspiel mit den Gliedern Sperren(unausgewogenen Knoten) und Entsperren(Vorfahr) eintritt. (Dabei bedeutet Sperren(Knoten) eigentlich Sperren(Teilbaum). Und: bei einer potentiellen Rebalancierung muss die Sperre sogar eine Ebene höher gehalten werden.) Ist der Einfügepunkt erreicht, kann auch schon mit der Korrektur- und Entsprerrschleife begonnen werden – für maximale Parellelität wieder in einer Kind-Elter-Kette.
    Wenn alle nebenläufigen Mitspieler diese Gerichtetheit des Sperrprotokolls einhalten, kann eine zirkuläre Abhängigkeit und damit eine Verklemmung (deadlock) nicht entstehen.
  12. 4 kleine AVL-Bäume mit Rot-Schwarz-Ausstattung.
    Bei den geteilten Knoten gehen beide Farben.
    Behauptung: Hat der AVL-Baum eine gerade Höhe h, dann lässt er sich mit Schwarztiefe h2 bei schwarzer Wurzel einfärben; ist h ungerade, dann mit Schwarztiefe h+12 bei schwarzer oder mit h–12 bei roter Wurzel. (Die NIL-Knoten sind dabei nicht mitgezählt.)
    Beweis: Die Behauptung ist richtig für h ≤ 1 (siehe Abbildung).
    Bei größerem geradem h gibt es einen Kindbaum mit der ungeraden Höhe h–1, der sich nach Induktionsvoraussetzung mit roter Wurzel und Schwarztiefe h–22 einfärben lässt. Hat der andere Kindbaum dieselbe Höhe, so gilt für ihn dasselbe. Ist er niedriger, dann ist seine Höhe h–2 und gerade; er lässt sich also mit der gleichen Schwarztiefe (bei schwarzer Wurzel) einfärben. Nachdem die neue Wurzel schwarz eingefärbt ist, ist die Schwarztiefe im zusammengesetzten Baum h2 für alle Pfade.
    Bei größerem ungeradem h hat einer der Kindbäume die gerade Höhe h–1 und lässt sich mit schwarzer Wurzel und Schwarztiefe h–12 einfärben. Ist ein Kindbaum niedriger, dann ist seine Höhe h–2 und ungerade, lässt sich also mit glücklicherweise derselben Schwarztiefe h–12 und mit schwarzer Wurzel einfärben. Wird die neue Wurzel schwarz eingefärbt, dann ist die neue Schwarztiefe h+12. Da beide Kindbäume jedoch schwarze Wurzel haben, kann die neue Wurzel auch rot eingefärbt werden, in welchem Fall sich eine Schwarztiefe von h–12 ergibt.  ■
  13. Fügt man n Knoten in einen leeren AVL-Baum ein, so ist (wie beim Rot-Schwarz-Baum) der Aufwand A im Mittel proportional zu n. Es gibt aber auch Schlüsselfolgen, bei denen A schneller wächst, und zwar in etwa wie die Anzahl der Vergleiche, die für die Positionierung dieser Einfügungen nötig sind.
Dieser Artikel befindet sich in einer Auszeichnungskandidatur und wird neu bewertet, beteilige dich an der Diskussion!