AVL-Bäume

Werbung
© U.-P. Schroeder, Uni Paderborn
13
Rekonfigurierende binäre Suchbäume
Binärbäume, die zufällig erzeugt wurden, weisen für die wesentlichen Operationen Suchen,
Einfügen und Löschen einen logarithmischen Aufwand auf.
Damit kann man sich zufrieden geben.
Muß man jedoch mit der Möglichkeit rechnen, daß der Baum in nicht zufälliger, sondern z.B. in
sortierter Reihenfolge gefüllt wird, so droht eine Degeneration zur Liste und damit zu linearer
Komplexität für die Hauptoperationen.
Man kann Degeneration vermeiden, wenn man die Degeneration durch explizite Umordnungen im
Baum verhindert, d.h. die Teilbäume möglichst ausgewogen hält (Lastausgleich)
Diese Umordnungen sollen effizient sein, d.h. man möchte mit sowenig Operationen wie nötig
soviel Ausgleich wie möglich erzielen.
Für diese Rekonfiguration von Binärbäumen sind die sogenannten Rotationen als
Elementaroperationen vorgesehen
13-1
© U.-P. Schroeder, Uni Paderborn
13.1 Rotationen
Rotationen können an beliebigen Knoten eines binären Suchbaumes stattfinden, sofern die
betroffenen Teilbäume existieren
a
b
a
b
A
a
C
b
B
C
A
B
Einfachrotation linksherum (a) und rechtsherum (b): {A} a
{B} b {C}
•
Eine Linksrotation am Knoten a (Bezugsknoten) erfordert die Existenz
eines rechten Nachfolgers (b)
•
Eine Rechtsrotation am Knoten b (Bezugsknoten) erfordert die Existenz
eines linken Nachfolgers (a)
13-2
© U.-P. Schroeder, Uni Paderborn
Realisierung einer einfachen Linksrotation
durch Änderung der Verzeigerung
a
b
void s_rotate_left(element* &a)
{
// performs a single left rotation with regard to node a
element * b = a->right;
a->right = b->left;
b->left = a;
a = b;
}
void s_rotate_right(element* &a)
{
// performs a single right rotation with regard to node a
element * b = a->left;
a->left = b->right;
b->right = a;
a = b;
}
13-3
© U.-P. Schroeder, Uni Paderborn
Doppelrotation
Die Doppelrotation besteht aus zwei Einfachrotationen:
Beispiel: Doppelrotation links (im Gegenuhrzeigersinn)
Bezugsknoten
Bezugsknoten
a
⇒
c
A
b
B
b
a
D
C
⇒
b
A
c
B
C
a
A
c
B
D
1. Schritt
2. Schritt
Einfachrotation rechts am
rechten Nachfolger
Einfachrotation links am
Bezugsknoten
13-4
C
D
© U.-P. Schroeder, Uni Paderborn
Doppelrotation linksherum: {A} a {B}
b
{C}
c {D}
Bezugsknoten
a
b
⇒
c
A
b
B
D
a
A
c
B
C
In C++:
void d_rotate_left(element* &a)
{
// performs a double rotation anticlockwise
s_rotate_right(a->right);
s_rotate_left(a);
}
13-5
C
D
© U.-P. Schroeder, Uni Paderborn
Doppelrotation rechtsherum {A} a {B} b {C} c {D}
c
Bezugsknoten
a
b
D
b
A
B
⇒
a
A
C
In C++:
void d_rotate_right(element* &a)
{ //performs a double rotation clockwise
s_rotate_left(a->left);
s_rotate_right(a);
}
13-6
c
B
C
D
© U.-P. Schroeder, Uni Paderborn
Mit diesen vier Spielarten der Rotation lassen sich Unsymmetrien der Last aus der Sicht des
Bezugsknotens ausgleichen.
Ja nach Lage des überlasteten Teilbaums kann eine der vier Varianten eingesetzt werden.
Bezugsknoten
Überlasteter Teilbaum
Einfache
Doppelte Doppelte
Rechtsrotation
Einfache
Linksrotation
13-7
© U.-P. Schroeder, Uni Paderborn
Linksrotation
Anwendungsfall: Der rechte Teilbaum ist zu groß.
Einfachrotation: Innerhalb des rechten Teilbaums ist der rechte (äußere) Teilbaum zu groß
a
b
b
A
B
a
C
A
13-8
C
B
© U.-P. Schroeder, Uni Paderborn
Linksrotation
Doppelrotation: Innerhalb des rechten Teilbaums ist der linke (innere) Teilbaum zu groß
Bezugsknoten
a
b
⇒
c
A
b
B
D
a
A
C
13-9
c
B
C
D
© U.-P. Schroeder, Uni Paderborn
Rechtsrotation
Anwendungsfall: Der linke Teilbaum ist zu groß.
Einfachrotation: Innerhalb des linken Teilbaums ist der linke (äußere) Teilbaum zu groß
b
a
A
a
C
b
A
B
B
13-10
C
© U.-P. Schroeder, Uni Paderborn
Rechtsrotation
Doppelrotation: Innerhalb des linken Teilbaums ist der rechte (innere) Teilbaum zu groß
c
Bezugsknoten
a
b
D
⇒
b
A
B
a
A
C
13-11
c
B
C
D
© U.-P. Schroeder, Uni Paderborn
13.2 AVL-Bäume
Um die logarithmische Komplexität der Operationen zu erhalten, müssen
Gleichgewichtsbedingungen nicht nur für den Wurzelknoten, sondern für alle Knoten gelten.
Gleichgewicht nur bezüglich der Wurzel:
AVL-Bäume sind binäre Suchbäume, die eine gewisse Gleichgewichtsbedingung zwischen
Teilbäumen einhalten.
Sie sind benannt nach ihren beiden Erfindern Adelson-Velskii und Landis (1962)
Definition
Ein AVL-Baum ist ein binärer Suchbaum, in dem für jeden Knoten gilt:
Die Höhen h des linken und des rechten Teilbaums unterscheiden sich um höchstens 1.
Formal:
∆h ≤ 1
mit ∆ h : = hL − hR
13-12
© U.-P. Schroeder, Uni Paderborn
Minimaler AVL-Baum der Höhe 7
13-13
© U.-P. Schroeder, Uni Paderborn
Prinzip des AVL-Baums
Um die Gleichgewichtsbedingung einzuhalten und leicht überprüfen zu können,
ist es sinnvoll, in den Knoten zusätzlich die Höhe des jeweiligen Teilbaums speichern
struct element
{
int height;
value data;
element *left;
element *right;
};
//definition of AVL node element
//height of subtree defined by node
//data value
//left successor (or child)
//right successor (or child)
Nach dem Einfügen oder Löschen von Elementen werden die Höhen aktualisiert und alle
betroffenen Knoten entlang des Pfades von der Wurzel bis zur Einfüge- oder Löschstelle
bezüglich der Gleichgewichtsbedingung überprüft.
Bei Verletzung der Bedingung wird durch Rotation das Gleichgewicht wiederhergestellt.
13-14
© U.-P. Schroeder, Uni Paderborn
Hilfsfunktionen für AVL-Baum-Implementierung
int Max(int x, int y)
{ // returns max of x and y
if (x<y) return y; else return x;
}
int node_ht(element *node)
{ // returns height of node even if node is NULL
if (node == NULL) return -1; else return node->height;
}
void calc_height(element *node)
{//updates height of node assuming correct height of successors
node->height=1+Max(node_ht(node->left),node_ht(node->right));
}
element* get_min(element * node)
{
// returns pointer to minimum of subtree
while (node->left != NULL) node=node->left;
return node;
}
13-15
© U.-P. Schroeder, Uni Paderborn
Rotationsfunktionen
void s_rotate_right(element* &a)
{ // performs single right rotation and updates heights
element * b = a->left;
a->left = b->right;
b->right = a;
a = b;
calc_height(a->right);
calc_height(a);
}
void s_rotate_left(element* &a)
{ // performs single left rotation and updates heights
element * b = a->right;
a->right = b->left;
b->left = a;
a = b;
calc_height(a->left);
calc_height(a);
}
void d_rotate_left(element* &a)
{
s_rotate_right(a->right);
s_rotate_left(a);
}
void d_rotate_right(element* &a)
{
s_rotate_left(a->left);
s_rotate_right(a);
}
13-16
© U.-P. Schroeder, Uni Paderborn
Einfügen
Wir nehmen an, die Gleichgewichtsbedingung sei vor dem Einfügen an allen Knoten erfüllt.
Das Einfügen eines Knotens in einem Teilbaum läßt seine Höhe unverändert oder erhöht sie um +1.
60
20
h=3
60
h=1
h=2
h=0
80
h=1
10
40
h=0
30
h=0
70
h=0
90
20
h=2
h=0
10
h=1
40
h=0
30
50
Höhe ändert sich nicht
h=3
h=0
h=1
70
75
Höhe ändert sich
13-17
80
h=1
h=2
h=0
90
© U.-P. Schroeder, Uni Paderborn
Höhenänderung beim Einfügen: Fallunterscheidung
Falls eine Höhenänderung eintritt, können entlang des Einfügepfades
am jeweils betrachteten Knoten folgende Fälle unterschieden werden:
a)
Die Teilbäume des Knotens waren gleich hoch
Der neue Knoten ändert die Höhe eines der beiden Teilbäume um +1.
Die Gleichgewichtsbedingung wird eingehalten
b)
Die Teilbäume des Knotens waren ungleich hoch
Der neue Knoten wird im kleineren Teilbaum eingefügt und ändert dessen Höhe um +1.
Die beiden Teilbäume haben jetzt gleiche Höhe.
Die Gleichgewichtsbedingung wird eingehalten
c)
Die Teilbäume des Knotens waren ungleich hoch
Der neue Knoten wird im größeren Teilbaum eingefügt und ändert dessen Höhe um +1.
Die Höhen der beiden Teilbäume unterscheiden sich jetzt um 2.
Die Gleichgewichtsbedingung ist verletzt und muß durch Rotation wiederhergestellt werden
13-18
© U.-P. Schroeder, Uni Paderborn
Nur im Fall c) gibt es also etwas zu tun. Betrachten wir also wieder die möglichen Fälle.
(Das Ganze gilt analog für die spiegelbildliche Situation)
Gegebene Situation (Gleichgewichtsverletzung in a, rechter Teilbaum zu hoch: hb = hA + 2):
a
B
Fall I:
?
b
A
C
C ist schuld : hb = hC + 1
Linksrotation einfach
Situation:
Lösung:
b
a
a
b
A
B
A
C
13-19
C
B
© U.-P. Schroeder, Uni Paderborn
Fall II:
B ist schuld : hb = hB + 1
Linksrotation doppelt
Situation:
Lösung:
b‘
a
a
b
A
b‘
BL
C
A
BR
B
13-20
b
BL
BR
C
© U.-P. Schroeder, Uni Paderborn
Wirkung der Einfachrotation auf die Höhen
Nach Einfügen: h=x+3
Nach Rotation: h=x+2
a
b
h=x+2
b
x+1 A
C
x
x+1
⇒
a
x+1 A
B x
x
B
C
x
Stelle der
Lasterhöhung
Wirkung der Doppelrotation auf die Höhe
Nach Einfügen: h=x+3
Nach Rotation: h=x+2
a
c
h=x+2
b
D
x
x+1
⇒
x+1
x
c
A
x
x-1
B
x
C
x+1
b
x-1
x
Alternativ mögliche
Lasterhöhung
13-21
A
x
x-1 B
a
C x-1
x
D x
© U.-P. Schroeder, Uni Paderborn
Einfügen
void check_rot_left(element* &node)
{
if (node==NULL) return;
// empty subtree
else
if (node->right!=NULL)
// left rotation possible
if (node_ht(node->right)-node_ht(node->left)==2) //rotate
if (node_ht(node->right->left)>node_ht(node->right->right))
d_rotate_left(node); // double rotation
else
s_rotate_left(node); // single rotation
else calc_height(node);
// update node height
else calc_height(node);
// update node height
}
void check_rot_right(element* &node)
{
if (node==NULL) return;
// empty subtree
else
if (node->left!=NULL)
// right rotation possible
if (node_ht(node->left) - node_ht(node->right)==2)
if (node_ht(node->left->right)>node_ht(node->left->left))
d_rotate_right(node); // double rotation
else
s_rotate_right(node); // single rotation
else calc_height(node);
// update node height
else calc_height(node);
// update node height
}
13-22
© U.-P. Schroeder, Uni Paderborn
void ins(element* &p, value v)
{
if (p == NULL) // insert position found: create new node
{
p = new element;
p ->height = 0;
p ->left = NULL;
p ->data = v;
p ->right = NULL;
return;
}
if (v < p->data) // branch to left subtree
{
ins(p->left, v);
check_rot_right(p);
}
if (v > p->data) // branch to right subtree
{
ins(p->right, v);
check_rot_left(p);
}
// else element is already in the tree: nothing is being done
}
13-23
© U.-P. Schroeder, Uni Paderborn
Beispiel
Die Eigenschaft des AVL-Baums läßt sich gut an Hand eines sortierten Einfügens erkennen, das bei
einem normalen (freien) Binärbaum eine Degeneration zur Folge hätte.
Beispiel: Einfügen der Werte 10, 20, ... , 70 in einen am Anfang leeren AVL-Baum
10
20
10
20
20
10
10
30
30
40
20
40
10
20
40
30
50
10
40
50
30
20
60
13-24
10
60
30
50
70
© U.-P. Schroeder, Uni Paderborn
Entfernen
Das Entfernen verläuft ähnlich wie beim freien Binärbaum (Kap. 12). Wir verwenden jedoch eine
rekursive Formulierung.
Nachdem der zu entfernende Knoten gefunden wurde, werden je nach Existenz von Nachfolgern
vier Fälle unterschieden:
•
Hat der Knoten keinen Nachfolger, kann er direkt gelöscht werden.
•
Hat er nur einen linken Nachfolger, so wird sein linker Nachfolger an seinen Vorgänger
gehängt (Überbrückung). Dann kann er gelöscht werden.
•
Hat er nur einen rechten Nachfolger, so wird sein rechter Nachfolger an seinen Vorgänger
gehängt (Überbrückung). Dann kann er gelöscht werden.
•
Hat er zwei Nachfolger, so wird er durch den linkesten Knoten in seinem rechten
Teilbaum (sein Nachfolger in In-Ordnung) ersetzt (Werttransfer).
Dieser Knoten wird dann durch einen weiteren (rekursiven) Aufruf von remove gelöscht.
Bei jeder (rekursiven) Rückkehr aus remove muß eine Rotationsprüfung durchgeführt werden, da
eine Höhenänderung stattgefunden haben kann. Fallunterscheidung wie beim Einfügen.
13-25
© U.-P. Schroeder, Uni Paderborn
void rem(element* &node, value v)
{ element *p;
if (node == NULL) return; // (sub)tree empty: not found
else
if (v < node->data) rem(node->left, v); // go to left subtree
else
if (v > node->data) rem(node->right, v); // go to right subtree
else
// element found
{ if (node->left != NULL && node->right != NULL)
{
// two children
p=get_min(node->right); // min of right subtree
node->data = p->data; // value transfer
rem(node->right,node->data);//remove min of right subtree
check_rot_right(node);
}
else
{ p=node;
if (node->left==NULL && node->right==NULL)
{ delete p; node = NULL; }
else
{ if (node->left==NULL)
// only right child
{ node=node->right; check_rot_right(node);}
else
// only left child
if (node->right==NULL)
{ node=node->left; check_rot_left(node);}
delete p;
calc_height(node);
}
}
}
}
13-26
© U.-P. Schroeder, Uni Paderborn
Beispiel:
Aus dem folgenden AVL-Baum (a) werden die Elemente 10, 15, 20, 25, 30, 35, 40, 45, 50
sukzessive entfernt.
35
35
15
45
25
10
40
45
30
15
50
30
20
40
55
(a)
(b)
35
35
25
45
30
40
(c)
50
20
55
25
20
15
25
45
30
50
55
40
(d)
13-27
50
55
© U.-P. Schroeder, Uni Paderborn
45
50
35
30
45
40
50
35
40
55
55
(e)
(f)
45
50
40
45
50
(h)
55
(g)
50
(i)
55
55
55
(j)
13-28
© U.-P. Schroeder, Uni Paderborn
Komplexität der Operationen im AVL-Baum
Von den Rotationen abgesehen, verlaufen die Operationen
•
•
•
Suchen
Einfügen
Entfernen
wie bei den freien Binärbäumen, d.h. es muß ein Pfad von der Wurzel bis maximal zu einem Blatt
abgelaufen werden.
Da der Aufwand für eine Rotation oder Doppelrotation konstant ist (O(1)), d.h. nicht von der
Größe des Baumes abhängt, gilt - wie sonst auch in Bäumen - daß der Aufwand der drei
Operationen linear mit der Höhe des Baumes wächst.
Es bleibt also die Frage, wie die Höhe eines AVL-Baumes mit seiner Knotenzahl zusammenhängt.
13-29
© U.-P. Schroeder, Uni Paderborn
Höhe von AVL-Bäumen
Um eine Abschätzung zu erhalten, betrachten wir den ungünstigsten Fall,
d.h. AVL-Bäume mit einer für die gegebene Knotenzahl maximalen Höhe,
bzw. AVL-Bäume mit einer für eine gegebene Höhe minimalen Knotenzahl:
Diese Bäume genügen offensichtlich einem gewissen Bildungsgesetz
13-30
© U.-P. Schroeder, Uni Paderborn
Höhe von AVL-Bäumen
F0
F1
Fh
...
Fh-2
Fh-1
Der Baum Fh der Höhe h setzt sich also zusammen aus einem Baum der Höhe h-1 und einem
Baum der Höhe h-2.
Für die Entwicklung der Knotenzahlen nh = n( Fh ) gilt also:
nh = 1 + nh−1 + nh−2 mit n0 = 1 und n1 = 2
d.h. die Knotenzahlen der maximal asymmetrischen AVL-Bäume entsprechen (fast) den FibonacciZahlen.
Die Bäume heißen daher auch Fibonacci-Bäume.
Ein AVL-Baum der Höhe h hat daher mindestens fh+2 -1 Knoten,
wobei fk die k-te Fibonacci-Zahl meint.
13-31
© U.-P. Schroeder, Uni Paderborn
Höhe von AVL-Bäumen
Für einen allgemeinen AVL-Baum der Höhe h gilt demnach:
nh ≥ fh+2 −1
Für Fibonacci-Zahlen gilt die Abschätzung des Goldenen Schnitts:
k
1 1 + 5
fk >

 −1

5
2 
1 1 + 5
und daher nh ≥ fh+2 −1 >


5 2 
bzw. h <
h+2
−2
1
log2 [ 5 (nh + 2)] − 2
1 + 5
log2 

 2 
Im O-Kalkül also: h = O(log n)
Die Grundoperationen in AVL-Bäumen besitzen also höchstens logarithmischen Aufwand.
13-32
Herunterladen