Bäume Programmierstarthilfe WS 2010/11 Fakultät für Ingenieurwissenschaften und Informatik Organisatorisches Im Web unter http://www.uni-ulm.de/psh Vorwissen Du kannst mit Objekten umgehen und hast bereits mit verketteten Listen gearbeitet. Konzepte Auf diesem Blatt werden wir einen binären Suchbaum anlegen, dieser erweitert die Ideen verketteter Listen um die schnelle Auffindbarkeit von Elementen. Wir werden dabei sehen, dass rekursive Methoden praktisch und effizient zugleich sein können. 1 Wiederholung Zur Wiederholung des Vorwissens findest du hier Aufgaben, die du am besten schriftlich und direkt auf dem Blatt löst. Versuche zunächst, ob du die Lösung auswendig kennst. 1.1 Listen Vervollständige die Methode swap(Node a) der Klasse List. Sie soll das übergebene Element a mit seinem Nachfolger in der Liste vertauschen. Beispiel: Aus der Liste d-e-f-g-a–b-c würde durch Ausführen der Methode swap(a) die Liste d-e-f-g–b-a-c. Die Methode soll in jedem Fall funktionieren, also auch wenn das Element a das erste oder letzte der Liste ist, keinen Nachfolger hat oder nicht in der Liste enthalten ist. 1 2 3 4 public class Node{ public int value; public Node nf; } //Nachfolger 5 6 7 public class List{ public Node kopf; 8 9 public void swap(Node a) { Programmierstarthilfe //TODO: 10 Bäume Node a mit seinem Nachfolger vertauschen 11 } 12 13 } 2 Aufgaben 2.1 Binäre Suchbäume Schreibe eine Klasse BinBaumKnoten, mit der sich Knoten eines binären Suchbaums erstellen lassen. Jeder Knoten enthält einen int-Wert und Referenzen auf einen linken und einen rechten Nachfolgerknoten vom gleichen Typ. Du kannst dich dabei an der Liste vom letzten Mal orientieren. Schreibe außerdem eine Klasse BinBaum, die eine Referenz auf die Wurzel des Binärbaums enthält. a) Schreibe einen Konstruktor für die Klasse BinBaumKnoten, der einen int-Wert übergeben bekommt und einen Knoten mit entsprechendem Wert anlegt. b) Schreibe einen Konstruktor für die Klasse BinBaum, der einen int-Wert übergeben bekommt und einen neuen Baum erzeugt, dessen Wurzel ein Knoten mit dem übergebenen Wert erstellt. c) Schreibe eine rekursive Einfügemethode für die Klasse BinBaum. Sie bekommt einen intWert übergeben. Ist der Wert kleiner als der der Wurzel ruft sie sich rekursiv auf, allerdings mit dem linken Nachfolgerknoten, ansonsten mit dem rechten. Ist der entsprechende Nachfolger null wird an dieser Stelle ein neuer Knoten mit dem entsprechenden Wert eingefügt. Ist der Wert schon vorhanden, soll kein neuer Knoten eingefügt werden und false zurückgegeben werden. d) Schreibe eine Methode boolean contains(int wert), die überprüft ob ein IntegerWert im Baum vorhanden ist. Bei der Suche kannst du dich an der vorherigen Teilaufgabe orientieren. Die Methode soll rekursiv arbeiten. e) Schreibe für die Klasse BinBaum die rekursiven Methoden String preOrder(), String postOrder() und String inOrder(), welche den Baum in gegebener Weise traversieren (durchlaufen) und alle Werte als String mit Leerzeichen dazwischen ausgeben. 2.2 Binäre Suchbäume 2 Die Klasse BinBaum soll nun um weitere Methoden erweitert werden. Teste diese mit einem selbst erstellten Binärbaum. a) Schreibe eine Methode für den Binärbaum, welche den Knoten mit dem kleinsten Wert im Binärbaum sucht und dessen Wert zurückgibt. int smallestNode(). 2 Programmierstarthilfe Bäume b) Schreibe eine Methode int countLeaves(), welche die Anzahl der Blattknoten zählt und zurück gibt. c) Schreibe zur Ausgabe des Binärbaum eine Methode void printOut(), die diesen rekursiv auf der Konsole ausgibt. Die Ausgabe könnte etwa so aussehen (der Baum ist also um 90 Grad nach links gedreht): 10 7 6 5 4 3 1 Tipp: Vernachlässige zuerst die Einrückung. *d) Schreibe eine Methode BinBaum copy(), welche eine tiefe Kopie des Binärbaums zurückgibt. Tiefe Kopie bedeutet, dass keine Referenzen des alten Baums verwendet, sondern neue eigene BinBaumKnoten erzeugt werden. *e) Entwickle eine Methode void deleteNode(int wert), welche den Knoten mit dem gegebenen Wert löscht und dafür sorgt, dass die Ordnung des neuen Binärbaums gewährleistet bleibt. (* = schwierige Aufgabe) 3 Bonusaufgaben Durch die Aufgaben oben hast du ein Verständnis für das neue Konzept dieses Blatts bekommen. Durch Bonusaufgaben kannst du nun deine Kenntnisse vertiefen. 3.1 Huffman-Kodierung Statt einen Text (wie Huffman“) Buchstabe für Buchstabe mit jeweils 8 Bit abzuspeichern1 , kann ” man ihn kürzer speichern, indem man eine Kodierung nutzt 2 . Dabei wird ausgenutzt, dass nicht alle Buchstaben des Alphabets (oder gar tausende Sonderzeichen) im Text vorkommen und dass manche Buchstaben häufiger vorkommen als andere (z.B. hier f“). ” Eine beliebte Kodierung ist die sog. Huffman- oder Shannon-Fano-Kodierung3 , die auch in einigen ZIP-Formaten eingesetzt wird. Wir wollen nun einen Huffman-kodierten Text dekodieren: 10110110110111101101101101000001101101“ ” 1 etwa als 0000011101001000011101010110011001100110011011010110000101101110“ in UTF-8 ” als 110001010011111010“ mittels Huffman-Kodierung ” 3 siehe auch http://de.wikipedia.org/wiki/Shannon-Fano-Kodierung 2 etwa 3 Programmierstarthilfe Bäume Die Dekodierung läuft entlang des untenstehenden Baumes: Wir beginnen an der Wurzel. Die erste 1“ im Text bedeutet, dass wir im Baum nach rechts weitergehen sollen. Dann kommt eine 0“ ” ” und heißt, dass wir nach links weitergehen sollen. Wir haben dann ein Blatt erreicht, das mit dem Buchstaben B“ beschriftet ist. B“ ist damit der erste Buchstabe des dekodierten Textes (im kodier” ” ten Bitstring durch nur 2 Bit dargestellt). Wir gehen dann zurück an die Wurzel wiederholen den Vorgang vom 3. Bit an. Schreibe ein Programm, in dem der folgende Baum fest eingebaut ist und mit dem du den Text dekodieren kannst. 4