Beispiellösungen zu den ¨Ubungen Datenstrukturen und

Werbung
Robert Elsässer
u.v.a.
Paderborn, 29. Mai 2008
Beispiellösungen zu den Übungen
Datenstrukturen und Algorithmen
SS 2008
Blatt 6
Aufgabe 1 (6 Punkte):
Zunächst sollte klar sein, daß ein vollständiger Binärer Suchbaum ein AVL-Baum ist. Um
aus einem sortiertem Array einen vollständigen Binären Suchbaum zu erstellen, genügt es
das mittlere Element des Array als Wurzel des Baums zu nehmen und das Verfahren rekursiv
auf die zwei Hälften des Arrays anzuwenden.
SortArrayToAVL(A, l, r, y)
1 if l ≥ r
2 then p[node] ← y
3
key[node] ← A[r]
4
lc[node] ← nil
5
rc[node] ← nil
6
return node
7 else
8
q ← l+r
2
9
p[node] ← y
10
key[node] ← A[q]
11
lc[node] ← SortArrayT oAV L(A,l,q-1,node)
12
rc[node] ← SortArrayT oAV L(A,q+1,r,node)
13
return node
AVLLIN(A)
1 root[T ] ← SortArrayT oAV L(A,1,n,nil)
Die Korrektheit wird durch Induktion über die Höhe des Baums bzw. über die Länge des
Eingabearrays gezeigt. Zu zeigen ist, dass der Algorithmus bei Eingabe eines aufsteigend sortierten Arrays einen korrekten AVL-Baum konstruiert, der die Elemente des Arrays enthält.
I.A: Bei einem Array der Länge 1 gilt l ≥ r, also befindet sich der Algorithmus im if“-Fall.
”
Also wird das eine Element des Array als Wurzel des Baumes gesetzt. Der Parent und die
Kinder werden auf nil gesetzt. Dieser Baum mit einem Knoten und der Höhe 0 entspricht
einem korrektem AVL-Baum.
I.V.: Der Algorithmus konstruiert aus einem Array der Länge m ≤ n einen AVL-Baum der
Höhe blog mc.
I.S Für ein Array der Länge n ermittelt der Algorithmus als Wurzel das mittlere Element
des Arrays. Als linkes Kind erhält der Knoten den aus der vorderen Hälfte des Arrays konstruierten AVL-Baum, da alle Werte in dieser Hälfte kleiner sind als die Wurzel. Nach I.V.
wird dieser korrekt konstruiert, da die vordere Hälfte des Array eine Länge m ≤ n besitzt.
Gleiches gilt für das rechte Kind. Beide Teilbäume haben nach I.V. Höhe blog mc, da sich die
Länge der beiden Hälften des Arrays um max. 1 unterscheiden, kann sich auch die Höhe der
Teilbäume um max. 1 unterscheiden. Zudem sind beide Teilbäume AVL-Bäume, sodass der
so konstruierte Baum ebenfalls einem AVL-Baum entspricht.
Die Laufzeit ist bei einer Rekursiongleichung von T (n) ≤ 2T ( n2 ) + O(1) offensichtlich O(n).
Aufgabe 2 (6 Punkte):
Behauptung: Ein AVL-Baum ist nach dem Einfügen eines Knotens wieder ein korrekter
AVL-Baum.
Als erstes führt man folgendes Lemma ein:
Lemma: Nach dem Aufruf der Operation AVLEinfügen(T,n) ohne Ausführung der Operation Balance wird die Höhe des AVL-Baumes nicht verändert oder um 1 erhöht.
Beweis des Lemma:
Sei die Höhe des Baumes h. Da es sich um einen AVL-Baum handelt, unterscheidet sich die
Höhe des linken und des rechten Kindes jedes Knotens maximal um 1, insbesondere auch bei
der Wurzel des Baumes. Also ist die Höhe des linken beziehungsweise die des rechten Kindes
der Wurzel h − 1 oder h − 2.
OBdA wird das einzufügende Element ohne Balance dabei auszuführen in den linken Teilbaum eingefügt.
Fall 1: Die Höhe des linken Teilbaums der Wurzel ist h − 1.
Fall 1.1: Das Element wird an einen Knoten angefügt.
Dadurch ändert sich die Gesamthöhe des Teilbaumes nicht, weil die Höhe des
Knotens kleiner der Gesamthöhe des Teilbaums sein muss, ansonten würde es sich
um ein Blatt handeln. Also ändert sich auch nicht die Höhe des Baumes an sich.
Fall 1.2: Das Element wird an einem Blatt angefügt.
Fall 1.2.1: Das Blatt befindet sich auf einer Höhe kleiner h − 1. Dadurch ändert sich die
Gesamthöhe des Teilbaumes nicht. Also ändert sich auch nicht die Höhe des
Baumes an sich.
Fall 1.2.2: Das Blatt befindet sich auf der Höhe h − 1. Dadurch steigt die Höhe es Teilbaums um eins auf h, da ganz unten am Teilbaum angefügt wurde. Demnach
erhöht sich auch die Höhe des Baums an sich um eins auf h + 1.
Fall 2: Die Höhe des linken Teilbaums der Wurzel ist h − 2.
Fall 2.1: Das Element wird an einen Knoten angefügt.
Dadurch ändert sich die Gesamthöhe des Teilbaumes nicht, weil die Höhe des
Knotens kleiner der Gesamthöhe des Teilbaums sein muss, ansonten würde es sich
um ein Blatt handeln. Also ändert sich auch nicht die Höhe des Baumes an sich.
Fall 2.2: Das Element wird an einem Blatt angefügt.
Fall 2.2.1: Das Blatt befindet sich auf einer Höhe kleiner h − 2. Dadurch ändert sich die
Gesamthöhe des Teilbaumes nicht. Also ändert sich auch nicht die Höhe des
Baumes an sich.
Fall 2.2.2: Das Blatt befindet sich auf der Höhe h − 2. Dadurch steigt die Höhe es Teilbaums um eins auf h − 1, da ganz unten am Teilbaum angefügt wurde. Die
Höhe des Baums ist demnach immer noch h.
Das Einfügen eines Elements am rechten Teilbaum funktioniert analog.
Daraus folgt die Korrektheit des Lemma, da sich die Höhe des Baums beim Einfügen eines
Elements ohne Ausführen von Balance entweder nicht verändert oder maximal um 1 steigt.
Mit Hilfe des Lemmas kann man die Behauptung durch vollständige Induktion über die Höhe
h des Baumes beweisen.
Induktionsanfang: h = 0
Nach dem Einfügen eines Elements befindet sich außer der Wurzel ein Element im Baum.
Also ist die AVL-Eigenschaft erfüllt.
Induktionsvorraussetzung:
Ein AVL-Baum mit der Höhe < h ist nach dem Einfügen eines Elements wieder ein AVLBaum.
Induktionsschritt: h − 1 → h
Besitze der Baum T vor dem Einfügen des Elements u die Höhe h. Im folgenden sei y die
Wurzel von T , x = lc[y], A der linke Teilbaum von x, B der rechte Teilbaum von x und C der
rechte Teilbaum von y. Mit D bezeichnen wir den linken Teilbaum von y, also den Teilbaum
mit Wurzel x. Der Knoten u wird zunächst an die korrekte Position in einen der beiden Unterbäume von y eingefügt. O.B.d.A. nehmen wir an, dass u im linken Teilbaum von y, also
in D eingefügt wird. C und somit auch die Höhe von C bleiben daher unverändert (C bleibt
ein AVL-Baum). Da die Höhe von D < h beträgt, ist D nach Induktionsvorraussetzung auch
nach dem Einfügen von u weiterhin ein AVL-Baum. Nach obigem Lemma erhalten wir für
die Höhe von D (unmittelbar vor Ausführung der Operation Balance) folgende Fälle:
Fall 1: Die Höhe von D bleibt unverändert
Da T vor dem Einfügen ein AVL-Baum war und C nicht verändert wird, bleibt der Wurzelknoten von T ausbalanciert und T besitzt insgesamt weiterhin die AVL-Eigenschaft
und in Balance wird nichts verändert.
Fall 2: Die Höhe von D erhöht sich um 1. Wir erhalten folgende Fälle:
Fall 2.1: h [D] ∈ {h [C] , h [C] + 1}
Die AVL-Eigenschaft ist auch in der Wurzel y von T erfüllt und T somit insgesamt
ein AVL-Baum. In Balance wird wiederum nichts verändert.
Fall 2.2: h [D] > h [C] + 1
Da T vor dem Einfügen ein AVL-Baum war gilt: h [D] > h [C] + 2
Das bedeutet, dass die AVL-Eigenschaft nun in jedem Knoten von T, außer der
Wurzel erfüllt ist. T ist also ein beinahe-AVL-Baum.
Wir erhalten wiederrum 2 Fälle:
Fall 2.2.1: h [A] = H, h [B] ∈ {H, H − 1}, h [C] = H − 1
In diesem Fall gilt also: h [A] ≥ h [B].
Die Abfrage in Zeile 3 von Balance ergibt somit false und der Algorithmus
führt eine Rechtsrotationum y aus (Zeile 5, Abbildung 3).
Neuer Wurzelknoten dieses Teilbaums wird dadurch x.
Das linke Kind von x bleibt unverändert. Somit bleibt die Höhe des linken
Teilbaums von x h [A] = H. y wird rechtes Kind von x. Durch die Rechtsrotation wird B linker Teilbaum von y und C rechter Teilbaum. Somit gilt für
die Höhe von y nun
h [y] =
=
∈
=
max{h [lc [y]] + 1, h [rc [y]] + 1}
max{h [B] + 1, h [C] + 1}
{max{H − 1 + 1, H − 1 + 1}, max{H + 1, H − 1 + 1}}
{H, H + 1}
Die Höhe des linken und rechten Teilbaums von x unterscheiden sich nun um
höchstens 1. Somit ist nun der Teilbaum mit Wurzel x ein AVL-Baum.
Also ist auch der gesamte Baum wieder ein AVL-Baum.
Fall 2.2.2: h [A] = H − 1, h [B] = H, h [C] = H − 1
In diesem Fall gilt: h [A] < h [B].
Die Abfrage in Zeile 3 von Balance ergibt somit true und der Algorithmus
führt eine Linksrotation um x aus (Zeile 4). Durch die Rotation erhalten wir
den in Abbildung 5 dargestellten Baum. Dabei bezeichne z die Wurzel des
Teilbaums B und B’ und B” jeweils den linken und rechten Teilbaum von z.
Dabei gilt für die Höhen von B’ und B”:
h [B 0 ] = H − 1 und h [B 00 ] = H − 1 oder
h [B 0 ] = H − 2 und h [B 00 ] = H − 1 oder
h [B 0 ] = H − 1 und h [B 00 ] = H − 2
Im Anschluss daran, wird in Zeile 5 eine Rechtsrotation um x ausgeführt.
Um herauszufinden, ob der entstandene Teilbaum die AVL-Eigenschaft besitzt, muss man die Höhe der beiden Teilbäume von z errechnen.
Für die Höhe von x gilt:
h [x] =
∈
=
=
max{h [A] + 1, h [B 0 ] + 1}
{max{H − 1 + 1, H − 2 + 1}, max{H − 1 + 1, H − 1 + 1}}
{max{H, H − 1}, H}
{H}
Für die Höhe von y gilt:
h [y] =
∈
=
=
max{h [B 00 ] + 1, h [C] + 1}
{max{H − 2 + 1, H − 1 + 1}, max{H − 1 + 1, H − 1 + 1}}
{max{H − 1, H}, H}
{H}
Die Höhe des linken und rechten Teilbaums von z sind nun gleich.
Somit ist der entstandene Teilbaum mit Wurzel z ein AVL-Baum.
Demnach ist auch der gesamte Baum wieder ein AVL-Baum.
Damit ist gezeigt, dass ein AVL-Baum nach dem Einfügen eines Elements wieder ein AVLBaum ist.
Aufgabe 3 (6 Punkte):
Der Suchbaum wird um das Feld leftsize[x] erweitert, leftsize[x] enthält die Baumgröße des
linken Teilbaums mit der Wurzel x. Laufzeit und Korrektheit für Insert und Delete bleiben
dabei erhalten. Bei der Operation INSERT muss bei jedem passierten Knoten, in dessen
linken Teilbaum derKnoten eingefügt wird, leftsize um 1 erhöht werden.
Die Anzahl der Element in dem Intervall ermittelt man indem die Anzahl der Elemente
bestimmt die kleiner bzw. kleiner gleich der Grenzen des Intervalls sind und dann die Differenz
bildet.
LESSOREQUAL(x, k)
1 c←0
2 if x = nil
3 then
4
return 0
5 elsif k ≥ key[x]
6
then
7
c ← lef tsize[x] + LESSOREQU AL(rc[x], k)
8
else c ← LESSOREQU AL(lc[x], k)
9
return c
INTERVAL(x, y)
1 lessx ← LESSOREQU AL(root[T],x-1)
2 leqy ← LESSOREQU AL(root[T],y)
3 return leqy − lessx
Die Laufzeit von LESSOREQUAL ist O(log(n)), da jeder Knoten entlang eines Suchpfades
von der Wurzel zu einem Blatt besucht wird.
Die Korrektheit wird durch Induktion bewiesen. Der Algorithmus LESSOREQUAL zählt
dabei alle Zahlen kleiner gleich k.
I.A.: Bei einem Suchpfad der Länge 0 (Höhe 1), befindet man sich in einem Blatt, das heißt
beim nächsten rekursiven Aufruf ist x = nil und der Algorithmus gibt 0 zurück. Die Anzahl
der Zahlen kleiner gleich k ermittelt sich dann als lef tsize[x] + 0, wenn k ≥ key[x], wobei
leftsize[x] die Anzahl der Knoten mit Wert ≤ key[x] (Knoten im linkem Teilbaum) enthält,
oder 0, wenn k < key[x], was korrekt ist da dann der einzige Knoten im Baum größer als der
gesuchte Wert ist.
I.V.: Der Algorithmus ermittelt die korrekte Anzahl Knoten mit Wert kleiner gleich k für
Suchpfade der Länge bzw. Bäume der Höhe m < n.
I.S.: Bei einem Baum der Höhe n vergleicht der Algorithmus den aktuellen Knoten mit dem
Wert k. Ist der Wert des Knoten größer als k befindet sich der Algorithmus im else“ Fall.
”
Der Knotenn und alle Knoten im rechten Teilbaum sind also größer als k, also ermittelt sich
die Anzahl der Knoten mit Wert kleiner gleich k, als Anzahl der Knoten mit Wert kleiner
gleich k im linkem Teilbaum, für diesen rekursiven Aufruf reduziert sich die Höhe des Baumes
und die Azahl wird nach I.V korrekt ermittelt. Ist der Wert des Knoten kleiner gleich k, sind
dieser Knoten und alle Knoten im linkem Teilbaum kleiner gleich k. Die Anzahl dieser Knoten
entspricht leftsize[x]. Zusätzlich muss noch die Anzahl im rechtem Teilbaum ermittelt werden.
Hier gilt wieder die I.V.
Da LESSOREQUAL korrekt arbeitet, ist auch die Korrektheit von INTERVAL(x,y) gezeigt.
Aufgabe 4 (6 Punkte):
Eine Idee für eine Datenstruktur, mit der man dieses Problem realisieren kann, ist eine
AVL-Baum Struktur, bei der die einzelnen Knoten die Wagenmodelle repräsentieren. Um zu
erreichen, auch auf alte Baupläne eines Typs zugreifen zu können, lässt man die Knoten des
AVL-Baums auf Listen verlinken, wobei jeweils das erste Element der Liste den aktuellen
Plan enthält. Durch die Listenstruktur kann man dann über diesen auf die restlichen Pläne
zugreifen, indem man durch die Liste läuft.
Einfügen eines Bauplans:
Da ein Wagentyp durch einen Index repräsentiert wird, kann man zum Einfügen eines Bauplans neuen Wagentyps die Methode AVL-Einfügen leicht modifizieren, sodass sie als Element
eine einelementige Liste mit dem Bauplan an der richtigen Stelle im Baum einfügt. Da das
Erstellen dieser Liste in konstanter Zeit möglich ist, braucht auch der modifizierte Algorithmus nur Laufzeit O(log2 (n))
Falls der Bauplan eines Wagentyps aktualisiert werden soll, sucht man den richtigen Wagentyp mit der aus der Vorlesung bekannte Funktion AVL-Suchen() heraus. Jetzt erstellt man
ein neues Listenelement, das den aktuellen Bauplan enthält und hängt anschließend die gefundene Liste des Wagenmodelles dahinter. Anschließend fügt man die neue Liste an der
Stelle im Baum ein.
Das aktualisieren der Liste geht in konstanter Zeit, also ist die Laufzeit der Suche ausschlaggebend. Die ist O(log2 (n)).
Also geht das Einfügen bzw. aktualisieren eines Bauplans zu einem Wagenmodell in O(log2 (n)).
Suchen eines Bauplans:
Das Suchen eines Bauplans wird über die Funktion AVL-Suchen() realisiert, die laut Vorlesung O(log2 (n)) braucht. Man muss die Suche nicht modifizieren, da das erste Objekt der
Liste zurückgegeben werden muss und dieses immer der aktuelle Bauplan ist.
Herunterladen