Informatik I WS 05/06 Prof. Dr. W. May Dipl.-Inform. Oliver Fritzen Übungsblatt 11 Ausgegeben am: 20.01.2006 Abgabe bis: 31.1.2006 (Theorie) Dipl.-Inform. Christian Kubczak Thema: Algorithmen und Datenstrukturen: Bäume Die Theorie-Aufgaben bearbeiten Sie bitte und werfen die Lösungen zum oben genannten Termin in die Zettelkästen im Erdgeschoss der NAM. Eine Bearbeitung in Zweier-Teams innerhalb Ihrer Übungsgruppe ist möglich. Aufgabe 1 (Theorie: 10 Punkte): ADT Baum Auf den Folien der Vorlesung wurde der ADT Binärbaum mit den Operatoren is_empty, height, right, left und value vorgestellt. Fügen Sie noch eine Funktion size hinzu, die die Anzahl der Elemente im Baum berechnet. Lösung Signatur: size: BiBa<T> → Nat Axiome: size(create) = null size(BiBa(left, e, right)) = succ(plus(size(left), size(right))) Aufgabe 2 (Theorie: 15 Punkte): Traversieren eines Binärer Baums Durchlaufen Sie den rechts stehenden Baum 1. In-Order 2. Pre-Order 3. Post-Order Lösung 1. Bei Inorder wird zuerst rekursiv der linke baum besucht, dann der Knoten selbst und anschließend der rechte baum. D -> B -> E -> A -> F -> C -> G 2. Bei Preorder wird zuerst der Knoten selbst besucht und danach der linke und dann der rechte baum A -> B -> D -> E -> C -> F -> G 3. Bei Postorder werden erst beide bäume durchlaufen (erst links dann rechts), bevor der Knoten selbst besucht wird. D -> E -> B -> F -> G -> C -> A Aufgabe 3 (Theorie: 5+15+5=25 Punkte): Pre-Order, In-Order 1. Sie erhalten eine Liste von Zahlen, von der Ihnen gesagt wird, es handele sich um die (eindeutigen) Knotenschlüssel eines Binärbaums in Preorder- Reihenfolge. Können Sie daraus den ursprünglichen Baum eindeutig rekonstruieren? 2. Sie erhalten nun zwei Listen von Zahlen, einmal die Preorder- und einmal die InorderReihenfolge der Knoten desselben Binärbaums. Ist hier eine eindeutige Rekonstruktion möglich? 3. Funktioniert das Verfahren ohne Weiteres mit nicht binären Bäumen (wo also ein Knoten mehr als 2 Unterbäume haben kann)? Lösung 1. Nein, kann man nicht, da es zu einer Preorder-Sequenz mehrere Bäume geben kann. Beispiel: A B - C - D ist die Preordersequenz zu folgenden zwei - offenbar nicht gleichen - Bäumen: A / \ B A \ D B / \ / C C D 2. Wenn Pre-order- und In-order-Durchlauf gegeben sind, kann man tatsächlich mit folgendem Verfahren den Ursprungsbaum rekonstruieren: 1. Wähle das erste Pre-order-Element als Pivot-Element aus. 2. Teile die In-order-Liste in zwei Teile: links vom Pivot-Element und rechts vom Pivot-Element. 3. Wähle das nächste Element aus der Pre-order-Sequenz als Pivot-Element. Ist das Pivot-Element in der linken Liste, so ist es das linke Kind des vorigen Pivot-Elements. Gehe zu Schritt 2 mit dem Rest der Pre-Order-Liste als neuer Pre-Order-Liste, mit der linken Liste als neuer In-Order-Liste und mit dem aktuellen Pivot-Element. Ist das Pivot-Element in der rechten Liste, so ist es das rechte Kind des vorigen Pivot-Elements. Gehe zu Schritt 2 mit dem Rest der Pre-Order-Liste als neuer Pre-Order-Liste, mit der rechten Liste als neuer In-Order-Liste und mit dem aktuellen Pivot-Element. 3. Nein, der Begriff "inorder" wie er im Skript definiert ist, macht nur Sinn für binäre Bäume, wo ein "self"-Knoten nur max. 2 Unterbäume haben kann zwischen denen das "self"-Element steht. Gibt es mehr als 2 Unterbäume, ist nicht mehr klar zwischen welchen Ünterbäumen das "self"-Element stehen sollte (zumindest nicht ohne "inorder" umzudefinieren). Aufgabe 4 (Theorie: 12+12+16 = 40 Punkte): 1. Zeichnen sie einen Baum (der kein Binärbaum sein soll, sondern bei dem jeder Knoten mehrere Kinder haben kann) mit mindestens 15 Knoten. Bezeichen sie die Knoten in sog. Dewey-Notation (also z.B. 1.6.3.4 fuer das 4.Kind des 3. Kindes des 6. Kindes des Wurzelknotens). Nummerieren sie die Knoten in (a) preorder (in der Grafik farbig oder Zahl-in-Kreis) sowie (b) postorder (in der Grafik mit einer anderen Farbe oder Zahl-in-Rechteck). Legen sie außerdem eine Tabelle an mit folgenden Spalten: 1. Knoten-Nummer (Dewey) 2. Preorder-Nummer 3. Postorder-Nummer 4. Tiefe des Knotens (mit depth(root)=0) 5. Anzahl seiner Kinder 2. Sei x ein innerer Knoten ihres Baumes (z.B. Dewey-Nummer 1.3.2). Beschreiben sie - ohne Beweis - anhand der in der Tabelle abgelegten Daten, wie sie die folgenden Knotenmengen charakterisieren können (ohne Verwendung der Dewey-Notation): den Vaterknoten (parent) alle Vorfahren bis hin zur Wurzel (ancestors) seine Kindknoten (children) alle Nachfahren (d.h. Kinder, Kindeskinder etc: descendants) seine Geschwisterknoten (siblings) 3. Beweisen sie: post(x) = pre(x) - depth(x) + ||descendants(x)|| * * - Hinweis: pre(x) bezeichnet in diesem Zusammenhang nicht den Operator auf dem ADT Baum, der einen ADT Baum in einen ADT Liste umwandelt, sondern hier liefert pre(x) den preorder-Wert des Knotens x als Zahl (also genau die Zahl, die sie zuvor an jeden Knoten ihres Baumes als Zahl-im-Kreis geschrieben haben). post(x) analog. Lösung 1. Beliebiges Beispiel machen ... 2. 1. Vater (parent): {x | pre(x) < pre(self) AND post(x) > post(self) AND depth(x) = depth(self)-1} 2. Vorfahren (ancestors): {x | pre(x) < pre(self) AND post(x) > post(self) AND depth(x) < depth(self)} 3. Kinder (children): {x | pre(x) < pre(self) AND post(x) > post(self) AND depth(x) = depth(self)+1} 4. Nachfahren (descendants): {x | pre(x) < pre(self) AND post(x) > post(self) AND depth(x) > depth(self)} 5. Geschwister: {x | parent(x) = parent(self) } 3. Beweis durch Induktion, Beginn beim root-Element. Grundidee für den Induktionsschluss: Bei einem preorder-Durchlauf wird erst der Vaterknoten von x besucht, dann alle linken Geschwistern von x und deren Nachkommen, dann x selber. pre(x) ist also pre(parent(x)) + #linke Geschwistern + Summe aller Nachkommen aller linken Geschwistern (siehe (2)). Für postorder lässt sich eine ähnliche Gleichung aufstellen (siehe (3)). Außerdem ist klar dass sich die Nachkommen von parent(x) zusammensetzen aus den linken Geschwistern von x und deren Nachkommen, den rechten Geschwistern von x und ihren Nachkommen, sowie x selber und seinen Nachkommen. Mit diesen Informationen sowie der Induktionsbehauptung, dass die für x zu beweisende Aussage bereits für parent(x) gilt, kann man das Gleichungssystem lösen. Induktionsbeginn: Aussage "post(root) = pre(root) - depth(root) + ||descendants(root)||" gilt für root: Sei die Anzahl der Knoten im Baum insgesamt n. Dann gilt: post(root) = n pre(root) - depth(root) + ||descendants(root)|| 1 - 0 + (n-1) = n. fertig. parent(x) → x: Zu zeigen: post(x) = pre(x) - depth(x) + ||descendants(x)|| Annahme: post(y) = pre(y) - depth(y) + ||descendants(y)|| für y = parent(x) Es ist bekannt: depth(x) = depth(y) + 1 (1) pre(x) = pre(y) + ||L|| + ||desc(L)|| + 1 (2), wobei L die Menge aller linken Geschwister von x ist post(y) = post(x) + ||R|| + ||desc(R)|| + 1 (3), wobei R die Menge aller rechten Geschwister von x ist Damit und mit der Induktionsannahme kann man nun folgern: (3) ⇒ post(x) = post(y) - ||R|| - ||desc(R)|| - 1 (4) Wenn man (2) von (4) abzieht, erhält man: post(x) - pre(x) = post(y) - ||R|| - ||desc(R)|| - 1 - pre(y) - ||L|| - ||desc(L)|| - 1 = post(x) - pre(x) = post(y) - pre(y) - (||R|| + ||desc(R)|| + ||L|| + ||desc(L)||) - 2 (5) Außerdem gilt offenbar: ||desc(y)|| = ||L|| + ||desc(L)|| + 1 + ||desc(x)|| + ||R|| + ||desc(R)||, umgeformt: ||desc(y)|| - ||desc(x)|| - 1 = ||L|| + ||desc(L)|| + ||R|| + ||desc(R)|| (6) Einsetzen in (5) liefert: post(x) - pre(x) = post(y) - pre(y) - (||desc(y)|| - ||desc(x)|| - 1) - 2, umgeformt: post(x) = post(y) - pre(y) + pre(x) + ||desc(x)|| - ||desc(y)|| - 1 (7). Einsetzen der Induktionsannahme für post(y) auf der rechten Seite liefert: post(x) = (pre(y) - depth(y) + ||desc(y)||) - pre(y) + pre(x) + ||desc(x)|| - ||desc(y)|| - 1 = post(x) = - depth(y) + pre(x) + ||desc(x)|| - 1. (8) Einsetzen von (1) liefert: post(x) = - (depth(x) - 1) + pre(x) + ||desc(x)|| - 1 = post(x) = + pre(x) - (depth(x) ||desc(x)||. Fertig. Aufgabe 5 (Theorie: 10 Punkte): Binäre Suchbäume Gegeben sei folgender Suchbaum: Fügen Sie die "4" in den Baum ein und löschen Sie anschließend die "3" aus dem Baum. Lösung Löschen der "3": Einfügen der "4": Die "3" ist ein innerer Knoten mit zwei Kindknoten. Folglich muss der Knoten durch den am weitesten links stehenden Knoten des rechten Unterbaums ersetzt werden, da dieser in der Sortierreihenfolge der nächste ist.