Einführung in die Informatik – Algorithmen und Datenstrukturen Thema 16 – Datenstruktur Baum Baumstrukturen Bäume gehören zu den dynamischen Datenstrukturen. Sie entstehen, wenn ein Knoten mehrere Nachfolger hat. Danach ist ein allgemeiner Baum eine Struktur mit endlich vielen Knoten, wobei der erste Knoten keinen Vorgänger hat. Jeder andere Knoten hat genau einen Vorgänger und beliebig viele Nachfolger. Ist die Anzahl der Nachfolger auf zwei beschränkt, so heißt der Baum binär. Ein Baum wird als geordnet bezeichnet, wenn unter den Nachfolgern eines Knotens eine Anordnung definiert ist. Die Ordnung eines Baumes gibt an, wie viele Nachfolger ein Knoten maximal haben kann (Rang). 2 Baumstrukturen Die programmtechnische Realisierung kann durch Arrays oder durch Zeiger erfolgen. Bei der Array-Realisierung werden die Knoten eines Baumes als Elemente eines Arrays vereinbart. Beispiel: Huffmann-Codierung Bei der Zeiger-Realisierung wird die Beziehung zwischen einem Knoten und seinen Nachfolgern über Zeiger hergestellt: Beispiel: public class Element { protected Element links; protected Element rechts; protected Inhalt container; public Element() { container = new Inhalt(); links = null; rechts = null; } } 3 Definition Baumstrukturen Ein Baum kann als eine nicht leere Menge von Knoten und Kanten definiert werden, die gewissen Forderungen genügt. Ein Knoten ist dabei ein einfaches Objekt, welches verschiedene Informationen tragen kann. Eine Kante ist die Verbindung zwischen zwei Knoten. Als Pfad in einem Baum wird eine Liste aufeinander folgender Knoten bezeichnet, die im Baum durch Kanten verbunden sind. Der erste Knoten wird als Wurzel bezeichnet. Eine Grundvoraussetzung dafür, dass eine Datenstruktur als Baum bezeichnet wird, ist, dass von der Wurzel zu jedem beliebigen Knoten genau ein Pfad besteht. Sind mehr als ein Pfad möglich, so wird die Datenstruktur als allgemeiner Graph bezeichnet. 4 Datenstruktur - Baum (W urzel) (Kante) ( Pfad K11 - K32 ) (innerer Knoten) (Blatt) (äußerer Knoten) 5 Datenstruktur Baum - Grundbegriffe Jeder Knoten besitzt genau einen Knoten, der sich unmittelbar über ihm befindet und der als sein Vorgänger (parent) bezeichnet wird. Die Knoten, die unmittelbar auf einen Knoten folgen, werden als direkte Nachfolger (children) bezeichnet. Als Geschwister werden die Knoten bezeichnet, die den gleichen direkten Vorgänger haben. Knoten ohne Nachfolger werden auch Blätter oder Endknoten genannt. Jeder Knoten ist die Wurzel eines Unterbaumes, welcher aus dem Knoten selbst (Wurzel) und seinen Nachfolgern besteht. Eine Menge von Bäumen wird als Wald bezeichnet. 6 Datenstruktur Baum - Grundbegriffe Es besteht die Möglichkeit, die Knoten eines Baumes in Ebenen (Stufen) einzuteilen. Die Ebene eines Knotens ist die Anzahl der Knoten auf dem Pfad von ihm zur Wurzel (ohne ihn selbst). Die Summe der Ebenen aller Knoten des Baumes wird als Pfadlänge eines Baumes bezeichnet. Muss jeder Knoten eine bestimmte Anzahl direkter Nachfolger haben, für die eine bestimmte Reihenfolge festgelegt ist, so wird der Baum als n-ärer Baum bezeichnet. Der einfachste Typ eines n-ären Baumes ist der binäre Baum. Ein Baum heißt vollständig, wenn er auf jedem Niveau die maximal mögliche Knotenzahl hat und sämtliche Blätter die gleiche Tiefe haben. 7 Spezifikation des ADT Baum Die Beschreibung des ADT Baum erfolgt exakt über Import, Export, Wertebereich und Operationen. Import: Typen: TInhalt (als Inhaltstyp der Elemente) Export: Typen: TBaum TKante: links, rechts, oben (Aufzählungstyp) Wertebereich: Die Elemente der Datenstruktur sind hierarchisch angeordnet, d.h. jedes Element außer dem ersten (der Wurzel) hat einen direkten Vorgänger. Jedes Element hat null bis zwei direkte Nachfolger. Die Anzahl der Elemente ist durch den zur Laufzeit verfügbaren Speicherplatz begrenzt. Die Elemente enthalten den importierten Inhaltstyp. 8 Datenstruktur binärer Baum Danach hat ein binärer Baum folgende Merkmale: • Er besteht aus äußeren und inneren Knoten. • Innere Knoten haben genau zwei direkte Nachfolger (linker und rechter). • Äußere Knoten haben keine Nachfolger. • Er ist ein geordneter Baum. Für einen Knoten p gilt: Die Schlüssel im linken Teilbaum von p sind sämtlich kleiner als der Schlüssel von p und dieser ist kleiner als sämtliche Schlüssel im rechten Teilbaum von p. 9 Datenstruktur binärer Baum Ein binärer Baum ist leer, wenn er nur aus einem äußeren Knoten besteht. Er wird als voller binärer Baum bezeichnet, wenn jede Ebene vollständig mit inneren Knoten aufgefüllt ist. Eine binäre Baumstruktur ist immer rekursiv und geordnet. 10 Datenstruktur binärer Baum „Ein binärer Baum besteht aus einem Element (der Wurzel) und aus zwei binären Bäumen (dem linken und dem rechten Teilbaum), oder es ist der leere binäre Baum. Ein binärer Baum besteht aus endlich vielen Elementen.“ Definition des binären Baumes über Rekursion. 11 Datenstruktur binärer Baum Wurzel W L linker Nachfolger 12 R rechter Nachfolger Datenstruktur binärer Baum 13 Baum erzeugen Erzeugen: Methode vor nach Erzeuge (TBaum [aus], TInhalt[ein]) Es existiert ein Baum (als Ausgangsparameter) mit der Wurzel TInhalt und zwei leeren Teilbäumen. /** * Methode initBaum * Initialisieren der Datenstruktur Baum */ public void initBaum() { aktuell = null; wurzel = null; } 14 Einketten eines Elementes Hänge an: Methode vor nach 15 HaengeAn (TBaum [ein/aus], TKante[ein], TBaum[ein]) Der Baum im ersten Parameter ist nicht leer und nicht voll. Der Baum im ersten Parameter enthält den Baum im zweiten Parameter als linken oder rechten Teilbaum, je nach dem Parameter TKante. (TKante=oben ist ohne Bedeutung) Erzeugen eines binären Baumes Erzeugen eines binären Baumes und Einfügen von Elementen 1. Erzeugen und Initialisieren eines neuen Objektes 2. Test, ob Element erstes Element des Baumes ist (wurzel = null), wenn ja, dann für das erste Element die Wurzel des Baumes auf Adresse des Elementes setzen 3. Einordnen des neuen Elementes in die Baumstruktur nach einer vorgegebenen Relation, indem solange in der Baumstruktur verzweigt wird, bis entsprechend der Relation der Eintrag für die Nachfolgerkomponente null ist 4. Herstellen der Verkettung, indem der entsprechende Eintrag der Vorgängerkomponente auf die aktuelle Adresse gesetzt wird 16 Inhalt eines Knotens ausgeben Inhalt holen: Methode Lies (TBaum [ein], TInhalt[aus]) vor TBaum ist nicht leer. nach Im zweiten Parameter wird der Inhalt der Wurzel des Baumes TBaum zurückgegeben. /** * Methode ausgabeElement * Ausgeben des Inhaltes eines Baumobjektes */ public void ausgabeElement() { if (aktuell != null) aktuell.container.ausgabe(); } 17 Ändern des Inhaltes eines Knotens Ändern: Methode vor nach 18 Aendere (TBaum [ein/aus], TInhalt[ein]) TBaum ist nicht leer. Der Inhalt der Wurzel von TBaum ist mit dem Inhalt TInhalt überschrieben. Löschen eines teilbaumes (Teil-)Baum löschen: Methode vor nach 19 loesche (TBaum [ein/aus]) TBaum ist nicht leer. Der Baum TBaum (evtl. auch als Teilbaum) ist leer Test ob Baum leer Ist Baum leer?: Methode Leer (TBaum [ein]):boolean vor TBaum existiert. nach Falls der Baum TBaum leer ist, wird als Funktionsergebnis True zurückgegeben, ansonsten False. /** * Methode baumLeer * gibt true zurueck, wenn der Baum leer ist */ public boolean baumLeer() { if (wurzel==null) return(true); else return(false); } 20 Test ob Baum voll Ist Baum voll?: Methode vor nach 21 Voll :boolean Falls im Speicher kein Platz mehr für einen neuen Baum (mit zwei leeren Teilbäumen) ist, wird als Funktionsergebnis True zurückgegeben, ansonsten False. Löschen in einem binären Baum Der zu löschende Knoten ist ein Endknoten. Der auf den Knoten weisende Zeiger wird auf null gesetzt und der Speicherplatz wird freigegeben. Der Knoten hat nur einen Nachfolger. Die Adresse des zu löschenden Elementes in seinem Vorgänger wird durch die Adresse des Nachfolgers aktualisiert. Das Element wird gelöscht. Der Knoten hat zwei Nachfolger. Das in der Prozedur LOESCH verwendete Verfahren ersetzt den zu löschenden Knoten durch seinen linken Nachfolger und fügt den rechten Teilbaum an den am weitesten rechts stehenden Knoten an. 22 Durchlaufen eines binären Baumes Das vollständige durchlaufen aller Knoten eines binären Baumes ist notwendig, wenn z.B. die Summe der Schlüssel bzw. die Anzahl der Knoten usw. ermittelt werden soll. Es ist weiterhin notwendig, um die Baumstruktur vollständig in eine andere Datenstruktur zu überführen. Das vollständige Durchlaufen eines Baumes wird als Traversieren bezeichnet. Es gibt 3 Grundformen: • Hauptreihenfolge (Preorder) • Nebenreihenfolge (Postorder) • symmetrische Reihenfolge (Inorder) 23 Durchlaufen eines binären Baumes Durchlaufen aller Knoten eines Binärbaumes mit Wurzel p in Hauptreihenfolge: 1. Besuche die Wurzel p; 2. durchlaufe den linken Teilbaum von p in Hauptreihenfolge; 3. durchlaufe den rechten Teilbaum von p in Hauptreihenfolge. Durchlaufen aller Knoten eines Binärbaumes mit Wurzel p in Nebenreihenfolge: linker Teilbaum, rechter Teilbaum, Wurzel 24 Durchlaufen eines binären Baumes Durchlaufen eines binären Baumes in symmetrischer Reihenfolge nach dem LWR-Verfahren (Traversieren) Führe die folgenden Schritte rekursiv aus: 1. Bearbeite den Linksbaum, wenn er nicht leer ist. Breche die Bearbeitung ab, wenn der Baum leer ist. 2. Gib den aktuellen Knoten aus. 3. Bearbeite den Rechtsbaum, wenn er nicht leer ist. Vorteil: Die Knoten werden entsprechend der vorgegeben Relation geordnet ausgegeben. 25 Suchen in einem binären Baum Suchen von Elementen in einen binären Baum Führe die folgenden Schritte rekursiv aus: 1. Vergleiche den eingegebenen Wert mit dem Schlüssel der Wurzel. Besteht Gleichheit, breche ab. 2. Ist der eingegebene Wert kleiner als der Schlüssel der Wurzel, gehe zum linken Unterbaum. Besteht Gleichheit, breche ab. 3. Ist der eingegebene Wert größer als der Schlüssel der Wurzel, gehe zum rechten Unterbaum. Besteht Gleichheit, breche ab. 4. Ist der aktuelle Unterbaum leer, so gibt es keinen Schlüsselwert, der mit dem eingegebenen Wert übereinstimmt. 26 Struktur eines binären Baumes Ein binärer Baum mit N inneren Knoten hat N+1 Blätter. Seine Höhe kann maximal N sein und muss mindestens log2(N+1) sein. Die Höhe des Baumes ist ein wesentliches Kriterium für die Grundoperationen „Suchen“, „Einfügen“ und „Löschen“. Der Aufwand zum Suchen eines Elementes kann zwischen N und log2(N+1) liegen. Suchbäume sollten eine minimale Höhe haben 27 Höhenbalancierte Bäume AVL-Bäume „Ein binärer Suchbaum ist AVL (nach Adelson,Velskij und Landis 1962) -ausgeglichen oder höhenbalanciert, wenn für jeden Knoten des Baumes gilt, dass sich die Höhe des linken Teilbaumes von der Höhe des rechten Teilbaumes von p höchstens um 1 unterscheidet.“ Damit wird gesichert, dass AVL-Bäume mit N inneren Knoten und N -1 Blättern eine Höhe von O (log N) haben. Wird ein neues Element in einen AVL-Baum eingefügt, so ist es möglich, dass der neue Baum kein AVL-Baum mehr ist. Es muss eine neue Höhenbalancierung ausgeführt werden. (Möglichkeit: Umstrukturierung durch Rotation bzw. Doppelrotation) 28 Balancieren eines binären Baumes Möglichkeit: vollständige Umstrukturierung des Baumes 1. Überführen des Baumes in eine geordnete Liste 2. Aufbau der Baumstruktur durch Einfügen des rekursiv jeweils mittleren Elementes in die abgeglichene Baumstruktur 29 Bruderbäume Bruder-Bäume Bruder-Bäume werden auch als expandierte AVL-Bäume bezeichnet. Die Besonderheit dieser Bäume liegt darin, dass innere Knoten auch nur einen Sohn haben dürfen. „Ein binärer Baum heißt Bruderbaum, wenn jeder innere Knoten einen oder zwei Söhne hat, jeder unäre Knoten einen binären Bruder hat und alle Blätter dieselbe Tiefe haben.“ 30 Gewichtsbalancierte Bäume Gewichtsbalancierte Bäume Ein Baum wird als gewichtsbalanciert bezeichnet, wenn der linke und der rechte Teilbaum von p nicht zu unterschiedliche Größe haben. Daraus resultiert, dass sich die Anzahl der Knoten bzw. Blätter im linken und rechten Teilbaum sich nicht zu stark unterscheiden dürfen. Für jede Wurzel eines Teilbaumes wird das Gewicht berechnet. Ist T ein Baum mit W(T) Blättern, dessen linker Teilbaum TL W(TL) Blätter hat, so wird der Quotient p(T ) WW((TT )) als Wurzelbalance von T L bezeichnet. Für die Wurzelbalance wird eine entsprechende Grenze festgelegt. Gewicht: Anzahl der Blätter des Teilbaumes 31 Kontrollfragen 1. 2. 3. 4. 5. 32 Erläutern Sie die Datenstruktur Baum. Durch welche Eigenschaften wird ein Baum zum binären Suchbaum? Wie werden binäre Bäume erzeugt und verwaltet? Beschreiben Sie die Vorgehensweise beim Erzeugen eines Elementes und der Einordnung sowie beim Löschen von Elementen in einem binären Baum. Was versteht man unter Traversieren eines Baumes? Beschreiben Sie ein Traversierverfahren. Wie wird die Suche in binären Bäumen realisiert? Welche Bedeutung hat in diesem Zusammenhang der Abgleich von Bäumen? Wann wird ein Datenstruktur Baum als höhenbalanciert bezeichnet? Mit welchem Ziel wird die Höhenbalancierung durchgeführt? Beschreiben Sie ein Verfahren, mit dem ein beliebiger binärer Baum in einen höhenbalancierten Baum überführt wird. Literatur /BALZERT96/ /BARNES09/ /HEROLD10/ /RATZ10/ 33 Balzert, Helmut; Lehrbuch der Softwaretechnik; Spektrum Verlag, 1996 David J. Barnes, Michael Kölling Java lernen mit BlueJ Pearson Studium, 2009, ISBN-13: 978-3-86894-001-5 Herold, Helmut; Lurz, Bruno; Wohlrab, Jürgen Grundlagen der Informatik, Pearson Studium, 2007 ISBN 987-3-8273-7305-2 Ratz, Dietmar; Scheffler, Jens; Seese, Detlef; Wiesenberger, Jan; Grundkurs Programmieren in JAVA; Carl-Hanser-Verlag, München, Wien; 2010 ISBN 3-935042-00-0