Teil III : Bäume - mathematik.uni-ulm.de · 3 REPEAT pop UNTIL (: alle Operatoren auf dem stack...
-
Upload
trankhuong -
Category
Documents
-
view
219 -
download
0
Transcript of Teil III : Bäume - mathematik.uni-ulm.de · 3 REPEAT pop UNTIL (: alle Operatoren auf dem stack...
1
Teil III : Bäu m e
§ Was ist ein Baum?
§ Termbäume
§ Repräsentation allgemeiner Bäume
§ Travesieren von Bäumen
§ Binäre Suchbäume
K. Murmann, H. Neumann, Fakultät für Informatik, Universität Ulm, 2001
1. Was ist ein Baum?
• Stammbaum• Definitionen• Begriffe & Eigenschaften (Teil 1)• Anwendung von Bäumen• Begriffe & Eigenschaften (Teil 2)
2
Der Stammbaum einer Person besteht aus einem Knoten mit den Daten dieser Person, an den zwei wei tere Stammbäume angehängt s ind: der Stammbaum des Vaters und der Stammbaum der Mutter
Stammbaum
Dieser Baum ist ein Binärbaum , denn jeder Knoten besitzt zwei Nachfolger
In einem Binärbaum besitzt jeder Knoten maximal zwei Nachfolger
Definition 1:Ein Baum T mit Elementen eines Typs ist entweder leer oder er besteht aus einem Element (Knoten, Node), verknüpft (Kante(n), Edge(s)) mit einer endlichen Anzahl disjunkter Bäume.
Definition(en)
Definition 2:Ein Baum T ist eine endliche Menge N mit Elementen eines Typs mit folgenden Eigenschaften:
Ø die Menge N ist entweder leer oder
Ø es gibt ein ausgezeichnetes Element, die sog. Wurzel (root) des Baumes T
Ø die übrigen Elemente zerfallen in disjunkte Mengen, die ebenfalls Bäume bilden
3
Beispiel:
Knotenmenge (Nodes): N = {a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p}
Baum (Tree): T = {a, {b, {d, {i}}, {e, {j}, {k}, {l}}}, {c, {f, {o}}, {g, {m}, {n}}, {h, {p}}}}
T = {a, {b, {d, {i}}, {e, {j}, {k}, {l}}}, {c, {f, {o}}, {g, {m}, {n}}, {h, {p}}}}
Definition 3:
Ein Baum T ist ein Tupel
T = (N, E)
mit der Menge N von Knoten(-punkten)
und der Menge E der Kanten(-linien)
∞<≤Ν∈= )(0},|{ NcardinN i
NNE ×⊆
4
Begriffe & Eigenschaften (Teil 1)
Ø Ein besonderer Knoten ist die Wurzel (root) r: sie besitzt - im Gegensatz zu einem „normalen“ Knoten - keinen Vorgänger (parent) p sondern nur 0 ... k Nachfolger (children) c i
p
r
r
c
p
cX (p,c) ∈ Ep
c1 c2 c3 ck...Geschwister (siblings)
Wurzel (root) r:
Vorgänger (parent) p,Nachfolger (children) c i
Geschwister:
1)(},),(:|{ =∈∈∃= RcardErpNprR
}),(|{}{ 1 EcpNccccS iiikp ∈∧∈== K
ii cpchildpcparent
EcppcardRNc
===∈∈∀
)(,)(
1})),(|({:\}
Ø Jeder Knoten ausser der Wurzel hat genau einen parent (Vorgänger)
Ø Ein Baum ist zusammenhängend, d.h. man kann von einem beliebigen Knoten aus über eine Folge von parent-Knoten zur Wurzel „aufsteigen“. Ein „Aufstieg“ oder „Abstieg“ im Baum über m Knoten hinweg heisst Pfad mit Länge m-1.Die Höhe h eines Knotens ist die Länge des Pfades von der Wurzel bis zu diesem Knoten. Für jeden Knoten gibt es genau 1 Pfad von der Wurzel zu diesem Knoten. Die Höhe h des Baumes ist der längst-mögliche Pfad im Baum, also das Maximum aller Höhen.
Ø Ein „normaler“ Knoten (mit Vorgänger und Kind(ern)) heisst innerer Knoten, ein Knoten ohne Kind(er) heisst Blatt. Der Grad eines Knotens ist die Anzahl seiner Kinder. Ein Blatt besitzt
also den Grad 0 (deg(p)=0 ⇔ Knoten p ist Blatt). Der Grad eines Baumes T ist das Maximum der Knotengrade. Ist deg(T) ≤≤ 2, so ist T ein Binärbaum, ist deg(T) > 2, so ist T ein Mehrweg-/Vielwegbaum.
5
Ø Die Menge aller Kinder eines Knotens n bilden zusammen mit n denTeilbaum mit Wurzel n
Ø Ein Baum T heisst der Höhe nach ausgeglichen (height balanced), wenn die Höhe aller Teilbäume zu einem Knoten n (= Wurzel zu diesen Teilbäumen) sich maximal um 1 unterscheidet.Ein Baum T heisst dem Gewicht nach ausgeglichen (weight balanced) oder auch vollständig ausgeglichen, wenn sich die Teilbaum-Knoten-Anzahlen aller Teilbäume zu einem Knoten n (= Wurzel zu diesen Teilbäumen) maximal um 1 unterscheidet.
height balanced weight balanced
Anwendung von Bäumen
Ø UNIX-Dateisystem. Dieses ist im Prinzip ein Vielweg-Baum, dessen Kanten über die Links hergestellt sind.
Ø Datenorganisation (→ Zugriffsbeschleunigung)Die sortierte Datenmenge einer Datei sei im Hauptspeicher in einem „sortierten“ Baum abgelegt:
6
Für jeden Knoten gilt: Im linken Teilbaum nur kleinere, im rechten nur grössere Elemente.
(Binär-)Suche nach Element x: Vergleiche x mit Wurzel, falls grösser, dann rechter Teilbaum, falls kleiner, dann linker Teilbaum u.s.w.
Höhe des Baumes best immend für maximale Suchzeit; deshalb sollte der Baum möglichst ausgeglichen sein.
Suche, ob k in Datenmenge enthalten: k<m → k>g → k=k → k enthalten
Suche, ob z in Datenmenge enthalten: z>m → z>q → z>t → z≠v → z nicht enthalten
Ø Decodier(binär)baum für Morsezeichen
7
Begriffe & Eigenschaften (Teil 2)
Ø In geordnete Bäumen sind die Kinder-Knoten (Geschwister) eines parent-Knotens in einer zugewiesenen „horizontalen“ Ordnung sortiert.
p
c1 c2 c3 ck...zugewiesene Ordnung
Ø Ein ganzer Binärbäumen ist ein Binärbaum, bei dem alle Knoten mit weniger als 2 Kindern durch Blätter „binär ergänzt“ wurden; alle ursprünglichen Knoten sind dann innere Knoten (deg = 2), die Blätter sind alle vom Grad deg = 0. Der ganze Binärbaum muss nicht ausgeglichen sein.
Ø Ein vollständiger Binärbaum ist ein optimal ausgeglichener, ganzer
Binärbaum, alle möglichen Pfade Blatt → Wurzel sind gleich lang (= Höhe h des Baums)
Dieser vollständige Binärbaum (jeder Knoten 2 Kinder) besitzt:♣ 2h Blätter (hier 23= 8)♣ 20 + 21 + ... 2h-1 = geometr. Reihe = (2h - 1)/(2-1) = (2h - 1) innere Knoten
Höheh = 3
Tiefe
0
1
2
3
Ø Ein vollständiger k-stelliger Baum (jeder Knoten k Kinder) besitzt: ♣ Anzahl Blätter = kh
♣ Anzahl innerer Knoten = k0 + k1 + ... kh-1 = geometr. Reihe = (kh - 1)/(k-1)♣ eine Höhe h = logk(Anzahl Blätter)
8
1. Term-Bäume
• Notation arithmetischer Ausdrücke• Transformation eines arithmetischen Ausdrucks in
Postfix-Notation• Generierung von Analyse-Bäumen („parse-trees)
Notation arithmetischer Ausdrücke
Ø Infix-Notationist Standard-Notation mathematischer Ausdrücke
• 1-stelligen Operatoren: <aus> = <op><operand><op> = + | –
• 2-stellige Operatoren: <aus> = <operand><op><operand><op> = + | – | • | /
• Strukturierung durch Klammern: ( | )
Ø Präfix-Notation für• 2-stellige Operatoren: <aus> = <op><operand><operand>
Ø Postfix-Notation für• 2-stellige Operatoren: <aus> = <operand><operand><op>• keine Strukturierung durch Klammern notwendig!!
→→ maschinelle Auswertung
9
•
+ –
A B C D
Ø Baum-Notation:
Beispiel:
Ø Infix-Notation: (A + B) • (C – D)
Ø Präfix-Notation: • + A B – C D
Ø Postfix-Notation: A B + C D – •
Transformation eines arithmetischen Ausdrucks in Postfix-Notation
Ø Der arithmetische Ausdruck sei in Infix-Notation gegeben (Standard-Notation)
A + (B – C)/D
Ø shunting-yard algorithm (Verschiebebahnhof-Algorithmus)
10
Ø Regeln des shunting-yard algorithmDie Zeichenfolge des Ausdrucks wird von rechts nach l inks bewegt; dabei wird jedes Symbol individuell evaluiert nach 5 Regeln:
1 Transfer: Operanden (am Anfang des Eingabestrings) →→ in Ausgabestring
2 push: linke (öffnende) Klammer →→ auf stack
3 REPEAT pop UNTIL (: alle Operatoren auf dem stack bis zur öffnenden Klammer →→ in Ausgabestring; dann werden beide Klammern gelöscht.
4 WHILE priority(top) >= operator DO pop: Operatoren auf dem Stack →→ in Ausgabestring bis das oberste Stack-Element von geringerer Priorität ist.
(steigende Priorität: ( →→ + – →→ • / )Falls Stack leer oder oberstes Stack-Element von geringerer Priorität, dann Operator →→ auf Stack (push)
5 WHILE NOT stack_empty DO pop: Input string leer ⇒⇒ alle Elemente auf Stack →→ Ausgabestring
11
Generierung von Analyse-Bäumen („parse-trees)
Ø arithmetische Ausdrücke: duale Operanden ⇔⇔ binäre Bäume
Ø Infix- & Postfix-NotationInfix: A • (((B + C) • (D • E) ) + F) ← für uns
Postfix: A B C + D E • • F + • ← für den Rechner mit Stack
Ø Analysebaum •
A +
• F
+ •
B C D E
12
Aufbau eines Analysebaums aus einer Eingabe in Postfix-Notation
TYPELINK = POINTER TO NODE;NODE = RECORD
info : CHAR;left, right : LINKEND;
VARx, z : LINK;c : CHAR;
PROCEDURE createLeaf() : LINK;VARleaf : LINK;
BEGINNEW(leaf);leaf^.left := leaf;leaf^.right := leaf;RETURN leaf
END createLeaf;
inforight
left
link
left
right
leaf
BEGINstackInit;z := createLeaf();
REPEATREPEATReadChar(c)
UNTIL c <> ; (* skip blanks *)
NEW (x);x^.info := c;IF (c = '*') OR (c = '+') THENx^.right := pop;x^.left := pop
ELSEx^.right := z;x^.left := z
END;
push(x)UNTIL EOLN
END ....
z
x
x
z
13
1. Repräsentation al lgemeiner Bäume
• allgemeine Bäume• reine bottom-up Baumbearbeitung• reine top-down Baumbearbeitung
14
Allgemeine Bäume
Ø Allgemeine Bäume besitzen Knoten mit variabler Anzahl von Kind-Knoten
Ø Die Repräsentation bestimmt die Art der Operationen und umgekehrt
reine bottom-up Baumbearbeitung
Ø Bäume, die nur von den Blättern aufwärts (bottom-up) bearbeitet werden
Ø Für jeden Knoten wird nur der Verweis zum parent-Knoten repräsentiert
parent
RECORDelem : elemtype;parent := LINK
END
Hinweis : Die Wurzel r zeigt auf sich selbst
r
15
Ø Einfache und effiziente Realisierung mittels Feldern:
info[.] : Information, z.B. Zeichenparent[.] : Index des parent-Knoten
Ø Beispiel:
E
T
A
M LP
A S
R E
E
1 2
3
4 5 6 7
8
9 11
10
k : 1 2 3 4 5 6 7 8 9 10 11
info[k] : A S A M P L E T R E E
parent[k] : 3 3 10 8 8 8 8 9 10 10 10
reine top-down Baumbearbeitung
Ø Verwaltung von Kind-Knoten ohne vorherige Allokation einer spezif ischen (maximalen) Anzahl.⇒⇒ verkettete Liste für Repräsentation der Knoten (variable Anzahl)
Ø Konzept: Knoten des Baums haben genau 2 Zeiger:♠ Zeiger L(inks) zum 1. (am weitesten links stehenden) Kind-Knoten♠ Zeiger R(echts) zu den (eigenen) Geschwister-Knoten♠ Der letzte Geschwister-Knoten zeigt zurück auf parent-Knoten
(→ Schleifen)
n
c
s1 ..... sk
Geschwister s1 ...sk von n
p
16
Ø Frage: Gibt es einen Unterschied zwischen dieser (Baum-)Struktur und binären Bäumen?
Antwort: Nein
Ø Beispiel:
E
T
A
M LP
A S
R E
E
E
T
A
M
L
P
A
S
R
E
E
1. Traversieren von Bäumen
• Traversieren von Bäumen allgemein• Strategie pre-order• Strategie in-order• Strategie post-order• Strategie level-order
17
Traversieren von Bäumen allgemein
Ø Travesieren eines Baumes bedeutet das systematische Absuchen eines Baumes, wobei jeder Knoten „besucht“ wird (visit). „Besuchen“steht für: auslesen, einlesen, vergleichen, markieren, ...
Ø Hier: elementare Operationen auf Binärbäumen
Ø Beispielbaum:
E
T
A
M L
P
A
S
R
E
E
Strategie pre-order
Ø Rekursive Strategie:
Wurzel → l inker Teilbaum → rechter Teilbaum
E
T
A
M L
P
A
S
R
E
E
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
visit(t);traverse(t^.L);traverse(t^.R)
ENDEND traverse;
z ist Zeiger auf Blatt
Knotenfolge:
Rekursionstiefe:
E
6
A
4
A
4
S
3
M
2
P
1
E
5
T
5
R
4
E
3
L
2
visit(t) zuerst
18
TYPELINK = POINTER TO NODENODE = RECORD
elem : <elemtype>;L, R : LINK (* Verzweigung L(eft) & R(ight*)
END
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
visit(t);traverse(t^.L);traverse(t^.R)
ENDEND traverse;
PROCEDURE traverse (t : LINK);
BEGIN push(t) WHILE NOT stack_empty DO
t := pop; visit(t);IF (t^.R <> z) THEN
push(t^.R) END; IF (t^.L <> z) THEN
push(t^.L) END
END
END traverse;
Ø nichtrekursive Pre-Order-Traversierung → Stackbasierte Lösung
(Annahme: StackInitialisierung außerhalb der Prozedur !)
Travesiere rechts
Travesiere links
pre-order
19
t
t^.L
t^.R
t^.L^.L
t^.L^.R
t^.R
t^.R
t^.L^.R
t^.R
visit(t) visit(t^.L) visit(t^.L^.L)
visit(t^.L^.R) visit(t^.R)
t
t^.Rt^.L
t^.L^.L
t^.L^.R
Ø Beispiel:
Strategie in-order
Ø Rekursive Strategie:
l inker Teilbaum → Wurzel → rechter Teilbaum
E
T
A
M L
P
A
S
R
E
E
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
traverse(t^.L);visit(t); traverse(t^.R)
ENDEND traverse;
z ist Zeiger auf Blatt
Knotenfolge:
Rekursionstiefe:
A
4
S
3
A
4
M
2
P
1
E
6
E
5
T
5
R
4
E
3
L
2
visit(t) in der Mitte
20
TYPELINK = POINTER TO NODENODE = RECORD
elem : <elemtype>;L, R : LINK (* Verzweigung L(eft) & R(ight*)
END
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
traverse(t^.L);visit(t);traverse(t^.R)
ENDEND traverse;
Sonderfall 2
Sonderfall 1
Ø nichtrekursive In-Order-Traversierung → Die stackbasierte Lösung ist
ein Beispiel dafür, dass die rekursive Programmierung viel eleganter ist (aber auch langsamer). In der stackbasierten Lösung sind 2 Sonder-fälle zu berücksichtigen.
21
Strategie post-order
Ø Rekursive Strategie:
l inker Teilbaum → rechter Teilbaum → Wurzel
E
T
A
M L
P
A
S
R
E
E
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
traverse(t^.L);traverse(t^.R);visit(t)
ENDEND traverse;
z ist Zeiger auf Blatt
Knotenfolge:
Rekursionstiefe:
E
6
S
3
A
4
M
2
A
4
P
1
E
5
T
5
R
4
E
3
L
2
visit(t) zuletzt
TYPELINK = POINTER TO NODENODE = RECORD
elem : <elemtype>;L, R : LINK (* Verzweigung L(eft) & R(ight*)
END
PROCEDURE traverse(t: LINK)
BEGINIF t <> z THEN
traverse(t^.L);traverse(t^.R);visit(t)
ENDEND traverse;
22
Strategie level-order
Ø Nicht-rekursive Strategie:
oben → unten und auf jedem level links → rechts
E
T
A
M L
P
A
S
R
E
E
Knotenfolge: EAASMP ETREL
PROCEDURE traverse (t : LINK);
BEGIN enqueue(t) WHILE NOT queue_empty DO
t := dequeue(t); visit(t);IF (t^.L <> z) THEN
enqueue(t^.L) END; IF (t^.R <> z) THEN
enqueue(t^.R) END
END
END traverse;
Ø nichtrekursive Level-Order-Traversierung → Queuebasierte Lösung
es existiert keine offensichtliche rekursive Lösung!
Ø Anpassung der (nicht-rekursiven, stack-orientierten) Pre-Order-Prozedur:
23
t
t^.Rt^.L
t^.L^.L
t^.L^.R
Ø Beispiel:
oben rein
Queue!
unten raus
1. Binäre Suchbäume
• Struktur• Aufwand• Implementierung des Baums• Einfügen eines Knotens• Löschen eines Knotens
24
Struktur
Ø Problem: Eine Menge von INTEGER/CARDINAL-Zahlen (oder an-deren Elementen, für die eine Ordnung gilt) soll eingelesen und so abgespeichert werden, dass für weitere Elemente möglichst schnell entschieden werden kann, ob diese zur eingelesenen Menge gehören oder nicht.
Ø Lösung: Die einzulesende Menge wird in einer binären Baumstrukturabgelegt, sodass jeder Knoten ein Element enthält und für jeden Teilbaum gilt:
Elemente im < Wurzelelement < Elemente imlinken Tei lbaum rechten Teilbaum
Ø Suchbaum
Ø Beispiel:
25
8 32
5 13 30 38
2 6 11 19 26 31 36 41
Aufwand
Ø Gegeben: Ein vollständiger Binärbaum T und eine Zahl x, die als Suchschlüssel dient.
Ø Verfahren:1 Vergleich von x mit Wurzelelement, wenn gleich, dann gefunden →→ fertig
2 wenn kleiner, suche im linken Teilbaum, wenn grösser, suche im rechten Teilbaum
3 falls x nicht gefunden wurde bis das erste Blatt erreicht wurde, ist x im Suchbaum nicht enthalten
Ø Aufwand im schlechtesten Fall (Durchsuchen der gesamten Höhe des Baumes) bei n Knoten:
♠ vollständiger Baum: ld(Anzahl Blätter) = ld (n+1) Suchschritte♠ entarteteter Baum (lineare Liste): n Suchschritte
25
Implementierung des Baumes
Ø Struktur:
TYPE elemtype = CARDINAL; LINK = POINTER TO NODE; NODE = RECORD
elem : elemtype; parent, L, R : LINK
END;
VAR el : elemtype;
tree, newNode : LINK;
Elemente sind Zahlen
Ø Einlesen von Elementen und Generierung von Knoten des Baumes
newNode
Einfügen eines Knotens(siehe nächste Folie)
: tree := NIL; el := 1; (* Abbruchwert = 0 *)
WHILE el <> 0 DO ReadCard(el); IF el <> 0 THEN NEW(newNode); newNode^.elem := el; newNode^.parent := newNode; newNode^.L := NIL; newNode^.R := NIL; InsertNode(newNode, tree)
END END;
:
26
In der 1. Suchrunde:vorläufiger parent = Wurzel
Ø Nicht-rekursiver Algorithmusgegeben sind: Zeiger x auf neuen Knoten und Zeiger tree auf Baum(wurzel)
♣ der neue Knoten newNode wurde im letzten Programm erzeugt und mit einem Element el versehen. Der parent-Zeiger zeigt auf newNode selbst, L- und R-Zeiger auf NIL. Der neue Knoten soll mittels InsertNode(newNode, tree) in den Baum eingefügt werden.
♣ If der Baum noch leer ist (tree=NIL), then wird der neue Knoten zur Wurzel (tree:=x), (fertig!), else zeigt dessen parent-Zeiger vorläufig auf die Wurzel (par := tree) und die Bool‘sche Variable nodeInsert wird auf FALSE gesetzt, d.h. der neue Knoten ist noch nicht im Baum plaziert.
♣ Suche nach der richtigen Posit ion des neuen Knotens im Baum:
♥ Vergleich der Elemente: if Knotenelement < parent-Element then Einbau des Knotens in den l inken Teilbaum des vorläufigen parent (else in dessen rech-ten Teilbaum).
♥ If der l inke Zeiger des vorläufigen parent bereits auf einen Knoten zeigt (par^.L<>NIL), then wird dieser zum neuen vorläufigen parent (par:=par^.L) und es geht von vorne los mit Elemente-Vergleich (while (NOT nodeInsert) -Schleife), else - Platz ist noch leer - wird der Knoten dort eingebaut: par^.L:=x; x^.parent:=par; nodeInsert.=TRUE (fertig!)
Einfügen eines Knotens
PROCEDURE InsertNode(x : LINK; VAR tree : LINK);
VAR par : LINK; nodeInsert : BOOLEAN;
BEGIN IF tree = NIL THEN (* leerer Baum *)
tree := x (* parent(x) = x *) ELSE
par := tree, nodeInsert := FALSE;
WHILE (NOT nodeInsert) DO IF (x^.elem < par^.elem) THEN
IF (par^.L <> NIL) THEN par := par^.L ELSE par^.L := x;
x^.parent := par; nodeInsert := TRUE
END ELSE
IF (par^.R <> NIL) THEN par := par^.R ELSE par^.R := x;
x^.parent := par; nodeInsert := TRUE
ENDEND (* IF *)
END (* WHILE *) END (* IF *)
END InsertNode;
Baum leer → Knoten x ist Wurzel
Baum nicht leer → Wurzel ist (vorläufiger) parent
Einbau von x in li. Teilbaum des (vorl.) parent
Einbau von x in re. Teilbaum des (vorl.) parent
WHILE-Schleife läuft, bis Knoten x eingebaut
27
Ø Rekursiver Algorithmusgegeben sind: Zeiger x auf neuen Knoten und Zeiger tree auf Baum(wurzel)
♣ der neue Knoten newNode wurde im letzten Programm erzeugt und mit einem Element el versehen. Der parent-Zeiger zeigt auf newNode selbst, L- und R-Zeiger auf NIL. Der neue Knoten soll mittels InsertNode(newNode, tree) in den Baum eingefügt werden (wie beim nicht-rekursiven Algorithmus).
♣ If der Baum noch leer ist (tree=NIL), then wird der neue Knoten zur Wurzel (tree:=x), (fertig!), else Vergleich der Elemente: if Knotenelement < tree-Element then Einbau des Knotens in den l inken Teilbaum, also Aufruf von InsertNode(x, tree^.L).Achtung: Die Variable tree bekommt in diesem Aufruf den Zeigerwert von tree^.L zu-gewiesen!, d.h. der Zeiger tree zeigt im rekursiven Algorithmus immer auf die Tei lbaumwurzel!Falls Knotenelement nicht kleiner tree-Element (else-Zweig), wir der Knoten in dessen rechten Teilbaum eingebaut, also Aufruf von InsertNode(x, tree^.R).
♣ Ist die richtige Position des Knotens gefunden, wird sein parent-Zeiger auf die letzte(!) Teilbaum-Wurzel (das ist ja sein parent exakt im letzten(!) Aufruf von InsertNode) gesetzt: x^parent := tree.
♣ Der rekursive Algorithmus ist bedeutend eleganter als der nicht-rekursive.
Baum leer → Knoten x ist Wurzel
Einbau von x in linken Teilbaum von tree
PROCEDURE InsertNode(x : LINK; VAR tree : LINK);
BEGIN IF tree = NIL THEN (* leerer (Teil) Baum *) tree := x
ELSE IF (x^.elem < tree^.elem) THEN
InsertNode(x, tree^.L) (*in linken Teilbaum *) ELSE
InsertNode(x, tree^.R) (*in rechten Teilbaum *) END;
IF (x^.parent = x) THEN x^.parent := tree
END END
END InsertNode;
Einbau von x in rechten Teilbaum von tree
Der parent-Zeiger des Knotens x wird mit der Adresse des Vorgängers belegt.
tree zeigt im rekursiven Algorithmus nicht immer auf die (Gesamt-)Baumwurzelsondern auf die Teilbaumwurzel, in die der neue Knoten x eingebaut werden soll.tree verändert sich bei jedem rekursiven Aufruf.Ist die richtige Stelle für den neuen Knoten x im Baum gefunden, zeigt tree genauauf den Vorgänger (parent)
28
25
8 32
5 13 26 38
2 6 11 19 30 36 41
31
Ø Beispiel:
Folge: 25 8 32 5 2 6 13 11 19 26 30 31 38 36 41
2
Ø Beispiel: Dieselben Elemente, diesmal der Größe nach geordnet
5
6
8
11
13
19
25
26
30
31
32
36
38
41
Folge: 2 5 6 8 11 13 19 25 26 30 31 32 36 38 41
29
Ø iterativer Algorithmusgegeben: Zeiger x auf zu löschenden Knoten und Zeiger tree auf Baum(wurzel)Es treten 3 Fälle auf:
0 der Knoten besitzt keine Nachfolger: x^.L=NIL und x^.R=NILIn diesem Fall wird der Vorgänger (parent) des Knotens modifiziert.
1 der Knoten besitzt einen Nachfolger: entweder x^.L=NIL oder x^.R=NILIn diesem Fall wird der Knoten herausgeschnitten (siehe Listen).
2 der Knoten besitzt zwei Nachfolger: weder x^.L=NIL noch x^.R=NILIn diesem Fall wird im rechten Teilbaum des Knotens immer l inks hinuntergegangen bis zum Knoten, der links ein Blatt hängen hat. Dieser Knoten hat folgende Eigenschaften:
♠ Sein Element ist größer als das Element des zu löschenden Knotens und alle Knotenelemente im linken Teilbaum des zu löschenden Knotens
♠ Sein Element ist das kleinste im rechten Teilbaum des zu löschenden Knotens♠ Sein Element folgt also in der Elementordnung dem Element des zu löschenden Knotens♠ Der Knoten besitzt höchstens einen Nachfolger
Dieser Knoten wird wie im Fall (0) oder (1) herausgeschnitten und an die Stelle des zu löschenden Knotens gesetzt.
Löschen eines Knotens
5
6
5
löschen
5
6
10
5
10löschen
keine Nachfolger: ein Nachfolger:
zwei Nachfolger: 32
26
24
28
22 25
35
32 40
29
löschen
ausschneiden
32
28
24
22 25
35
32 40
29
30
Ersetze Knoten old durch Knoten new
PROCEDURE ReplaceNode(old, new : LINK);
BEGIN old^.elem := new^.elem; new^.parent := new; new^.L := NIL; new^.R := NIL END ReplaceNode;
PROCEDURE SuccessorElem(x : LINK) : LINK;
BEGIN x := x^.R; IF (x <> NIL) THEN WHILE (x^.L <> NIL) DO x := x^.L
END END; RETURN x END SuccessorElem;
Ø Programm; zuerst zwei Hilfsprozeduren
Überschreibe altes Knotenelement mit neuem
Die Zeiger des neuen Knotens werden „ausgehängt“
Finde den Knoten mit dem nächstgrösseren Element
x zeigt auf den Knoten, für den der Knoten mit dem nächstgrößere Element gesucht werden soll .Ein Schritt nach rechts, dann solange Schritte nach l inks, bis der Zeiger x auf einen Knoten mit l inkem Blatt zeigt. Dieser Knoten enthält das nächstgrößere Element
Lösche Knoten xØ Programm; jetzt die Lösch-Prozedur (1. Teil)
Fall 0 oder 1: der Knoten x wird mit Zeiger del markiert, der Unterbaum von x wird an seiner Wurzel mit Zeiger subT markiert.
Fall 2: der Knoten mit dem nächstgrößeren Element wird mit Zeiger del markiert, des Knotens Unterbaum wird an seiner Wurzel mit Zeiger subT markiert.
PROCEDURE DeleteNode(x : LINK; VAR tree : LINK);
VAR del, subT : LINK;
BEGIN IF (x^.L = NIL) OR (x^.R = NIL) THEN del := x; IF (x^.L = NIL) THEN subT := x^.R
ELSE subT := x^.L END
ELSE del := SuccessorElem(x);subT := del^.R
END;
...
31
...
IF (x^.parent = x) THEN (* x = root(tree) ? *) tree := subT; IF (subT <> NIL) THEN subT^.parent := subT
END ELSE IF (del^.parent^.L = del) THEN del^.parent^.L := subT
ELSE del^.parent^.R := subT END; IF (subT <> NIL) THEN subT^.parent := del^.parent
END; IF (del <> x) THEN (* xKnoten ersetzen ? *) replaceNode(x, del)
END END; DISPOSE(del) END DeleteNode;
Ø Programm; die Lösch-Prozedur (cont‘d)
Löschen von del (Fall 0,1,2): falls del am linken(rechten) Zeiger seines parent hing, wird dort jetzt subT angehängt
del <> x gibt es nur im Fall 2: dort wird der Knoten mit dem nächstgrößeren Element gelöscht und dessen Element im Knoten x eingesetzt. (gleichbedeutend mit Löschen von x und ersetzen durch Knoten mit dem nächstgrößeren Element.
Falls die (Gesamt-)Baumwurzel gelöscht werden soll, dann wird der Baumwurzelzeiger tree umgesetzt auf den Knoten, auf den subT zeigt und eine neue Wurzel gebildet.
§ Ein Baum ist rekursiv definiert: An seiner Wurzel hängen (Teil-)bäume. Er besteht aus Knoten und Kanten. Jeder Knoten hat einen parent und 0 ... mehrere children. Anwendungen sind: übersichtl iche Darstellung, effiziente Suche, ...
§ Arithmetische Ausdrücke werden gewöhnlich in infix-Notation geschrieben oder als
Termbaum gezeichnet. Für die maschinelle Verarbeitung ist die (klammerlose)
postfix-Notation geeignet. Eine Transformation infix- → postfix-Notation ist mittels shunting-yard-algorithm möglich. Die Generierung eines Termbaums im Rechner geschieht mittels „Verzeigerung“ von Knoten-Records, die ein Element besitzen, eine Adresse (auf die ein Zeiger eines anderen Knoten-Records zeigen kann) und drei eigene Zeiger (parent, left child, right child) zum „Verzeigern“ mit anderen Knoten (Kanten)
§ Traversieren von Bäumen: Durchlaufen eines gegebenen Baumes entlang der Kan-ten, wobei jeder Knoten einmal besucht wird, um dort eine Operation durchzuführen (auslesen, einlesen, vergleichen, markieren, ...). 4 Verfahren zur Traversierungwurden vorgestellt: pre-order, in-order, post-order, level-order
§ Suchbäume sind aufgebaut aus Knoten mit Elementen (die sich ordnen lassen).
Einbau eines zusätzlichen Knotens: Dieser wird „über die Baumwurzel eingespeist“ und je nachdem ob sein Element kleiner/größer ist als das Wurzelelement, steigt der Knoten einen Links-/Rechtsschritt den Baum hinab, usw. Dies geschieht bis zu einem „leeren Ende“. Dort wird der Knoten „eingehängt“.Weitere Standard- Operationen: Löschen eines Knotens, Suchen etc .