EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Gliederung Kapitel 12 Zum Datentyp Heap –Beispiel eines Heaps –Eigenschaften eines Heaps –Definition des Heaps –Datentyp Heap –Zur Implementierung mittels eines Feldes Anwendungen –Heapsort –Prio-Warteschlangen, mit Heaps implementiert
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Beispiel: Heap Inhalt der Knoten Platznummer der Knoten
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Eigenschaften eines Heaps Ein Heap (Haufen) ist ein knotenmarkierter binärer Baum, so daß gilt: –(Die Markierungsmenge ist geordnet) im Beispiel: ganze Zahlen –Der binäre Baum ist links-vollständig: Alle Ebenen (vgl. Breitendurchlauf) sind vollständig besetzt (bis evtl. auf die letzte); die letzte Ebene ist "von links voll aufgefüllt". –Partiell angeordnete Markierungen: Auf jedem Pfad von der Wurzel zu einem Blatt ist die Menge der Knotenmarkierungen monoton steigend angeordnet.
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Definition: Heap Ein Heap (Haufen) ist ein knotenmarkierter binärer Baum, für den gilt: –(Die Markierungsmenge ist geordnet.) –Der binäre Baum ist links-vollständig. –Die Knotenmarkierung der Wurzel ist kleiner oder gleich der Markierung des linken resp. rechten Sohnes (, sofern vorhanden). –Die Unterbäume der Wurzel sind Heaps. --> An der Wurzel (Knoten 1) steht das kleinste/eines der kleinsten Element(e).
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Grobidee zum ADT Heap Datenstruktur Operationen –Init –Einfügen eines Elementes in einen Heap –Entfernen eines der kleinsten Elemente/des kleinsten Elementes aus dem Heap und Hinterlassen eines Rest-Heaps –Zudem: Heap ist leer ?/ Heap ist voll ? Drucken.....
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Implementierung über Feld Binärer Baum: Feld: Darstellung: Binärer Baum Feld
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Implementierung über Feld Beziehung: Baum- Feld-Darstellung : –Der Inhalt des i-ten Knotens in der Baumdarstellung wird im i-ten Feldelement abgelegt. Das bedeutet: Baum ebenenweise von links nach rechts eintragen. (Das Feldelement a[0] wird nicht verwendet!) Lemma: –Die Söhne des Knotens i in einem Heap haben die Knotennummern 2i : linker Sohn 2i + 1: rechter Sohn Beweis: Induktiv
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Implementierung über Feld Ein Feld a mit n ganzen Zahlen realisiert einen Heap, falls a[i/2] a[i] für alle i= 1,...., n gilt. –Dabei ist "/" als ganzzahlige Division zu verstehen. z.B. 5/2 = 2 In einem (Minimum-)Heap, realisiert durch das Feld a, steht das kleinste Element immer in a[1]. Animation:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Operation Einfügen: Idee Einfügen von 11 in unseren Heap neuer Knoten 11 Vergleich Vergleich Vergleich Beispiel:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Operation Einfügen: Algorithmus Eingabe: –Heap a mit n Elementen, neues Element x Ergebnis: –Heap a mit n+1 Elementen, x seiner Größe gemäß eingefügt Vorgehensweise: –Schaffe neuen Knoten n+1, setze a[n+1] = x (Füge also neuen Knoten mit Beschriftung x in den Heap (evtl.noch an falscher Stelle) ein). –Sortiere an richtige Stelle ein.
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Operation Einfügen "An richtige Stelle einsortieren" - Idee: einsortieren(k) : ist k=1, so ist nichts zu tun, ist k>1, so geschieht folgendes: falls a[k/2] > a[k], vertausche a[k/2] mit a[k], rufe einsortieren(k/2) auf. Hier ist nämlich die Heap-Bedingung verletzt. Anmerkungen: k/2 ganzzahlige Division k = 0 wird ausgelassen !
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Operation Einfügen void Heap::einsortieren(int Knoten_Nr) { if (Knoten_Nr > 1) { int DerVater_Nr = Knoten_Nr/2; if (HeapalsFeld[Knoten_Nr] < HeapalsFeld[DerVater_Nr]) { Tausche(&HeapalsFeld[Knoten_Nr], &HeapalsFeld[DerVater_Nr]); einsortieren(DerVater_Nr); } } } In das Feld HeapalsFeld wird richtig einsortiert gemäß:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Operation Entfernen Idee einer Hau-Ruck-Lösung: –Entfernen des kleinsten Elements –Baue einen neuen Heap ohne das erste Element auf –(z.B. durch sukzessives Einfügen der Elemente in einen neuen Heap.) Nachteil: –berücksichtigt nicht, daß vor dem Entfernen des kleinsten Elements ein Heap vorliegt. Idee einer effizienteren Lösung: –Verwende genau diese Information Programm:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Wie erzeugt man dann einen Heap? Beobachtung: jedes Blatt erfüllt die Heapbedingung Allgemeinere Situation: Wurzel erfüllt die Heap-Bedingung ? Unterbäume mögen die Heap- Bedingung erfüllen Vorgehen?
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Heap-Erzeugung Idee: Lasse die Wurzel einsinken: –Vergleiche den Eintrag im Knoten k mit denen seiner Söhne (,soweit vorhanden) –Falls der Eintrag im Knoten k kleiner oder gleich als diejenigen beider Söhne ist --> o.k. –Falls der Eintrag des Knotens k größer als der kleinere Eintrag beider Söhne ist: Ermittle den Sohn s mit dem kleineren Eintrag, Vertausche den Eintrag des Knotens k mit demjenigen des Sohnes s, Wiederhole die Überlegungen mit diesem Knoten s.
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Beispiel Heap-Bedingung verletzt Vergleich 6 68 ok
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Code zu "Erzeuge Heap", falls... void Heap::heapify(int Knoten_Nr) { int LinkerSohn_Nr = 2*Knoten_Nr, RechterSohn_Nr = 2*Knoten_Nr + 1, selektierter_Sohn; if (LinkerSohn_Nr <= AnzahlKnoten && RechterSohn_Nr > AnzahlKnoten) // es gibt keinen rechten Sohn {if (HeapalsFeld[LinkerSohn_Nr] < HeapalsFeld[Knoten_Nr]) Tausche(&HeapalsFeld[Knoten_Nr], &HeapalsFeld[LinkerSohn_Nr]); } else if (RechterSohn_Nr <= AnzahlKnoten) { // es existieren linker und rechter Sohn selektierter_Sohn = (HeapalsFeld[LinkerSohn_Nr] < HeapalsFeld[RechterSohn_Nr] ? LinkerSohn_Nr : RechterSohn_Nr); /* wähle den Sohn mit der kleineren Markierung aus. Bei Gleichheit wähle den rechten Sohn.*/
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Code zu "Erzeuge Heap", falls... Fortsetzung von void Heap::heapify if (HeapalsFeld[selektierter_Sohn] < HeapalsFeld[Knoten_Nr]) // ist Heap-Bedingung verletzt? { Tausche(&HeapalsFeld[Knoten_Nr], &HeapalsFeld[selektierter_Sohn]); heapify(selektierter_Sohn); }}} Programm:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Anmerkung zur Heapkonstruktion heapify kann dazu herangezogen werden, aus einem Feld A mit n Elementen einen Heap zu konstruieren: –die Blätter sind bereits Heaps, –hat Knoten k also Blätter zu Söhnen, so erfüllen seine Unterbäume die Heap-Bedingung, – durchlaufe die Knoten rückwärts von n/2 bis 1 und rufe heapify für jeden Knoten auf, – für n >= j >= n/2 ist der Knoten j ein Blatt Animation: Reheap
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Implementierung des ADTs Heap const int maxKnoten = 70;... struct Heap { public: void Init(); void Einfuegen(int); int Entfernen(); bool IstLeer(); bool IstVoll();int Kopf(); void Druck(); private: int HeapalsFeld[maxKnoten + 1]; /* Heap beginnt mit Index 1, Index 0 wird nicht verwendet! */ int AnzahlKnoten; void einsortieren(int); void Tausche(int *, int *); void heapify(int);}
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Anwendung 1: Heapsort Aufgabe: –Benutze Heap zum (effizienten) Sortieren. Heapsort arbeitet in zwei Phasen: – Aufbau eines Heaps aus einem Feld, –Schrittweiser Abbau des Heaps Auslesen des Wurzelelementes und Entfernen desselben Legen des "letzten" Elementes in die Wurzel Rekonstruktion der Heap-Bedingung für diese restlichen Elemente. Animation Programm
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Anwendung 2: PrioQueue mit Heaps Aufgabe: –Benutze Heap zum Implementieren einer Prioritätswarteschlange. Programm:
Kap 12: HeapVorl EINI-I"Prof. Dr. G. Dittrich Rückblick binäre Suchbäume Tiefendurchläufe durch einen binären Baum Breitendurchlauf durch einen binären Baum Studium von Warteschlangen, dabei Entdeckung von ADTs ADT Prioritäts- Warteschlange Studium von Heaps Heapsort