Binäre Bäume

Werbung
Binäre Bäume
Definition: Ein binärer Baum T besteht aus einer Menge von Knoten, die durch eine
Vater-Kind-Beziehung wie folgt strukturiert ist:
1. Es gibt genau einen hervorgehobenen Knoten r ∈ T , die Wurzel des Baums
2. Jeder Knoten außer r hat genau einen Vaterknoten
3. Die Wurzel r ist Vorfahre jedes Knotens.
4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.
In einem binären Baum unterscheidet man zwei Arten von Knoten: Blätter - das sind Knoten,
die kein Kind haben - und innere Knoten - das sind Knoten, die mindestens ein Kind haben,
wobei auch im Fall mit einem Kind festgelegt sein muss, ob dieses Kind ein linkes und ein
rechtes ist.
Wir sprechen von einem echten binären Baum, wenn jeder innere Knoten zwei Kinder hat.
Das folgende Beispiel zeigt links einen echten binären Baum, in der Mitte einen Baum, der
nicht binär ist, weil ein Knoten drei Kinder hat und rechts einen unechten binären Baum.
Wurzel
3 Kinder
nur ein Kind
Binäre Bäume können in Haskell als rekursiv definierter, algebraischer Typ implementiert
werden. Wir beginnen mit einem Datentyp BinTree zur Darstellung echter Binärbäume, für
die zwei Konstruktoren ausreichen: Ein Konstruktor Leaf für Binärbäume, die nur aus einem
Blatt bestehen und ein Konstruktor Node, der einen neuen Wurzelknoten als Vater über zwei
vorhandene Binärbäume stellt.
data
BinTree
=
Leaf |
Node
BinTree
BinTree
Zur Übertragung dieses Ansatzes auf unechte Binärbäume müsste man zwei zusätzliche Konstruktoren für Knoten mit nur einem rechten oder nur einem linken Kind einführen. Um das zu
vermeiden, behilft man sich mit einem Trick. Jeder Knoten des darzustellenden Binärbaums
T erhält soviele künstliche Blätter, dass er danch zwei Kinder hat (zweite Abbildung). Auf
diese Weise entsteht ein echter Binärbaum, dessen innere Knoten die ursprünglichen Knoten
von T sind. Da die neuen Blätter in T nicht vorhanden waren, benennt man sie mit Nil,
einem Ausdruck für das Nichts.
data
NTree
=
Nil |
Node
NTree
NTree
echter Binärbaum mit Nil−Knoten
nicht−echter Binärbaum
Nil−Knoten
Obwohl beide Definitionen bis auf die verschiedene Namesgebung gleich sind, ist die verbundene Interpretation anders. Für BinTree werden Blätter nur mit dem Konstruktor Leaf
erzeugt, in NTree stellt man ein Blatt als Node Nil Nil dar.
Häufig will man die Knoten eines Binärbaums mit bestimmten Werten (z.B. Zahlen oder
Zeichen) belegen. Das kann durch eine polymorphe Variation der Definition erreicht werden,
wobei der Typ a der Belegungsobjekte mit angegeben werden muss:
data
NTree
a
=
Nil |
Node
a
NTree
NTree
Tiefe, Höhe und Größe
Definition: Sei T ein Baum und v ∈ T ein Knoten. Der Abstand von v zur Wurzel
nennen wir die Tiefe von v und den Abstand von v zu seinem weitesten Nachfahren die Höhe
von v.
Die Höhe der Wurzel definiert die Höhe (und gleichzeitig die Tiefe) des Baums.
Im folgenden Satz werden die wichtigsten Fakten über die Anzahl von Blättern und inneren
Knoten in einem echten binären Baum zusammengestellt. Wir werden im Weiteren mit i(T )
und b(T ) die Anzahlen der inneren Knoten und der Blätter des Baums T bezeichnen. Die
Größe eines Baums ist die Gesamtzahl der Knoten. Sie ergibt sich durch n(T ) = i(T ) + b(T ).
Für die Höhe eines Knotens v bzw. des Baums T werden wir die Bezeichnungen h(v)
bzw. h(T ) verwenden.
Satz: Sei T ein echter binärer Baum der Höhe h. Dann gelten die folgenden fünf Bedingungen:
(1)
b(T ) = i(T ) + 1
(2)
h≤
i(T )
≤ 2h − 1
(3)
h+1≤
b(T )
≤ 2h
(4)
2h + 1 ≤
n(T )
≤ 2h+1 − 1
(5)
log2 (n(T ) + 1) − 1 ≤
h
≤
n(T )−1
2
Beweis: Zuerst beobachten wir, dass die Bedingung (3) unmittelbar aus (1) und (2) folgt
und (4) durch additive Verknüpfung von (2) und (3) entsteht. Die Ungleichungen in (5) folgen
aus (4) durch einafche Umformungen und Anwendung der Logarithmusfunktion:
n(T ) ≤ 2h+1 − 1 =⇒ n(T ) + 1 ≤ 2h+1 =⇒ log2 (n(T ) + 1) ≤ h + 1 =⇒ log2 (n(T ) + 1) − 1 ≤ h
und
n(T ) − 1
2
Bleibt nur noch der Beweis von (1) und (2), den man durch vollständige Induktion nach
h = h(T ) führt:
Der Induktionsanfang für h = 0 ist trivial, denn in diesem Fall besteht T nur aus der Wurzel,
die gleichzeitig das einzige Blatt ist. Somit ist b(T ) = 1, i(T ) = 0 und h = 0 wodurch beide
Bedingungen erfüllt sind.
Beim Induktionsschritt geht man davon aus, dass (1) und (2) erfüllt sind für alle Bäume, deren
Höhe kleiner als h ist (Induktionsvoraussetzung). Zum Nachweis der Induktionsbehauptung,
die die Gültigkeit von (1) und (2) für alle Bäume T der Höhe h beinhaltet, betrachtet man
zu einem solchen Baum T die Kinderknoten v1 und v2 der Wurzel r von T und die zwei
Teilbäume T1 und T2 die aus v1 mit allen seinen Nachfahren bzw. aus v2 mit allen seinen
Nachfahren entstehen. Offensichtlich ist sowohl h1 = h(T1 ) < h als auch h2 = h(T2 ) < h und
damit kann man die Induktionsvoraussetzung auf beide anwenden.
Während die Blätter von T durch disjunkte Vereinigung der Blättermengen von T1 und
T2 entstehen, muss bei den inneren Knoten einen zusätzlichen, nämlich die Wurzel von T
berücksichtigt werden:
2h + 1 ≤ n(T ) =⇒ h ≤
b(T ) = b(T1 ) + b(T2 ) und i(T ) = i(T1 ) + i(T2 ) + 1
Damit kann man unter Verwendung der Induktionsvoraussetzung Bedingung (1) wie folgt
ableiten:
b(T ) = b(T1 ) + b(T2 ) = (i(T1 ) + 1) + (i(T2 ) + 1) = i(T ) + 1
Auch die obere Schranke von i(T ) ergibt sich auf ähnliche Weise:
i(T ) = i(T1 ) + i(T2 ) + 1 ≤ (2h1 − 1) + (2h2 − 1) + 1 ≤ 2 · 2h−1 − 1 = 2h − 1
Die untere Schranke von i(T ) kann man auch ohne Induktionsvoraussetzung begründen: Nach
Definition der Höhe gibt es in T einen Weg der Länge h von Wurzel zu einem Blatt. Bis auf
den letzten Knoten auf diesem Weg sind alle Knoten innere Knoten und somit ist i(T ) ≥ h.
Traversierungen von Bäumen
Häufig werden Bäume als Datenstrukturen verwendet, indem entweder alle Knoten des Baums
oder nur die inneren Knoten oder nur die Blätter mit Zahlen oder anderen Objekten belegt
werden. Zur systematischen Auflistung dieser Objekte verwendet man sogenannte Baum–
Traversierungen, das sind Methoden mit denen alle Knoten des Baums besucht werden. Die
drei wichtigsten Standardmethoden sind:
• Preorder–Traversierung
• Postorder–Traversierung
• Inorder–Traversierung
Alle drei Verfahren kann man rekursiv beschreiben und als gemeinsame Regel gilt die Verankerung, dass im trivialen Fall, in dem der Baum nur aus einem Knoten besteht (die gleichzeitig Wurzel und das einzige Blatt ist), nur dieser Knoten zu besuchen ist.
Anderenfalls haben wir die Wurzel mit einem linken und einen rechten Teilbaum und verfahren
nach folgenden Regeln:
• Preorder: Besuche zuerst die Wurzel, dann traversiere den linken Teilbaum und danach
den rechten Teilbaum.
• Postorder: Traversiere zuerst den linken Teilbaum, danach den rechten Teilbaum und
besuche zuletzt die Wurzel.
• Inorder: Traversiere zuerst den linken Teilbaum, besuche dann die Wurzel und traversiere danach den rechten Teilbaum.
1
3
2
4
5
8
6
7
9
Für den abgebildeten Baum ergeben sich bei den drei Travesierungen die folgenden Besuchssequenzen:
• Preorder:
1, 2, 4, 5, 8, 9, 3, 6, 7
• Postorder:
4, 8, 9, 5, 2, 6, 7, 3, 1
• Inorder:
4, 2, 8.5, 9, 1, 6, 3, 7
Eine der wichtigsten Anwendungen von Inorder-Traversierungen ist mit dem Begriff des
binären Suchbaums verbunden:
Definition: Ein binärer Suchbaum speichert in seinen Knoten Zahlen und hat die Eigenschaft, dass für jeden inneren Knoten v alle Zahlen aus dem linken Unterbaum von v kleiner
oder gleich der Zahl in v sind und allen Zahlen im rechten Unterbaum von v größer oder gleich
der Zahl in v sind.
6
3
2
11
9
5
8
13
9
Für einen binären Suchbaum gibt der Inorder-Durchlauf die geordnete Folge der gespeicherten
Zahlen wieder. Das folgende Beispiel zeigt einen binären Suchbaum und mit den gestrichelten
Pfeilen die Inorder-Traversierung.
In einem binären Suchbäumen kann man sehr leicht überprüfen, ob eine bestimmte Zahl
x gespeichert ist. Man beginnt den Suchprozess in der Wurzel und vergleicht x mit Zahl a in
der Wurzel r. Es gibt 4 Fälle:
1. Ist x = a, dann hat man x bereits gefunden und ist fertig;
2. Ist x 6= a und r ist ein Blatt, dann kommt x im Baum nicht vor;
3. Ist r ein innerer Knoten und x < a, dann kann x nur im linken Teilbaum sein (dessen
Wurzel das linke Kind von r ist) und man wiederholt dort die Suche rekursiv.
4. Ist r ein innerer Knoten und x > a, dann kann x nur im rechten Teilbaum sein (dessen
Wurzel das rechte Kind von r ist) und man wiederholt dort die Suche rekursiv.
Die Suchzeit ist proportional zur Höhe des Baums, also O(h).
Herunterladen