Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen

Ähnliche Präsentationen


Präsentation zum Thema: "Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen"—  Präsentation transkript:

1 Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen
5.1   Zur Erinnerung: Datenstrukturen in Pascal Listenverarbeitung in Pascal 5.2   Abstrakte Datentypen und Objektorientierung Brüche als ADT Implementierung - objektorientiert Brüche als ADT Implementierung - funktional 5.3   Geordnete lineare Zusammenfassungen: Listen bzw. Folgen Cons-Zelle als Basisklasse Statisch-funktionale Listenimplementierung Listenimplementierung mit Objektmethoden 5.4   Stacks und Queues Zustandsorientierte Stack-Implementierung 5.5   Priority Queues 5.6   Find and Merge 5.6.1   Implementierung mit Arrays 5.6.2   Implementierung mit Bäumen

2 Implementierung von Queues:
Mit doppelt verketteter Liste als Zeigerstruktur Doppelt verkettet, da man auf beide Enden der Liste zugreifen muss. In einem Array Problem: Einfügen am Ende und Entfernen am Anfang führt dazu, dass die Queue „durch den Array wandert“. Lösung: Zyklisch in einem Array

3 Anwendungen von Queues
Realisierung von Warteschlangen, besonders in Betriebssystemen, z.B. Prozesse, Nachrichten, Druckaufträge.

4 5.5 Priority Queues: Mengen mit Insert und Deletemin
Priority Queues: Warteschlangen, in denen die Elemente Prioritäten haben. Es können Elemente mit beliebiger Priorität eingefügt werden und es wird immer das (ein) Element mit der höchsten Priorität entnommen. Anwendungen/Beispiele: Patienten im Warteraum einer Krankenhausambulanz Betriebssysteme, bes. Multiuser-Betriebssysteme

5 Oft können verschiedene Elemente gleiche Priorität haben: führt zu Multisets (Multimengen)
Multisets: Mengen mit Duplikaten (aber ohne Ordnung der Elemente, also keine Listen/Folgen). Beispiel: {|1,3,3,7,7,7,10|} auch notiert als: {(1,1),(3,2),(7,3),(10,1)} Zu einer Menge S sei M(S) die Menge aller Multimengen mit Elementen aus S. In Priority Queues: Prioritäten = natürliche Zahlen Höhere Priorität = kleinere Zahl (dann gibt es automatisch eine höchste Priorität).

6 Datentyp für die Priority Queue, algebraische Spezifikation:
algebra pqueue sorts   pqueue, elem ops     empty:                  pqueue       isempty: pqueue         boolean       insert: pqueue  elem  pqueue       deletemin: pqueue       pqueue  elem sets    pqueue = M(elem) functions       empty = {}       isempty (p)  = (p={})       insert (p,e) = p  {|e|}       deletemin (p) = (p',e)         mit e = min(p) und p'=p\{|e|} falls p{},          undefiniert sonst end pqueue.

7 Implementierung von Priority Queues
Z.B. mit partiell geordneten Bäumen. Ein partiell geordneter Baum (Heap) ist ein knotenmarkierter binärer Baum (T, m) (hier ist T der Baumgraph und m die Markierungsfunktion der Knoten), in dem für jeden Teilbaum (T ',m) mit der Wurzel x gilt:             für alle y aus T ' ist m(x)  m(y) In der Wurzel steht also jeweils das Minimum eines Teilbaumes. Analog möglich: Ordnung umgekehrt, also in jeder Wurzel eines Teilbaums das Maximum.

8 Definition Heap Speziellere Definition eines Heap:
partiell geordneter Binärbaum, der links-vollständig ist, d.h. die Ebenen werden von der Wurzel her gefüllt, und jede Ebene von links nach rechts. Außerdem: Implementierung in einem Array, in dem die Knoten in dieser Reihenfolge abgelegt werden.

9 Teilheap Ein Teil T [ i..k ] ( 0  i  k  n ) heißt Teilheap, wenn gilt:         für alle j aus {i,...,k} ist     m(T [ j ])  m(T [ 2j ])     falls 2j  k                                       und   m(T [ j ])  m(T [ 2j+1])  falls 2j+1  k Ist T [i+1..n] bereits ein Teilheap, so kann man durch Einsinkenlassen des Elements T [ i ] die Heapeigenschaft erweitern.

10 Aufbau des Heaps Zum Aufbau des Heaps werden die Elemente zunächst
in die Ebenen unterhalb der Wurzel eingebracht und zwar maximal 2 j  Elemente in die Ebene j (j = 0,..., k-1). Jedes Einsinkenlassen eines Elementes kostet den Aufwand O(k-1) = O(ld n), also bei n Elementen O(n ld n). Eine genauere Betrachtung zeigt, dass der Aufwand pro Element in jeder j-Ebene nur O(k-j-1) ist. Also erhält man allgemein         1 •2 k • 2 k (k-1) • 2 0  2 k-1 i > 0  i / 2i = 2 k = O(n) Hat man nun das oberste Element entfernt, so wird das letzte Element an seine Stelle gebracht und durch Einsinken- lassen (pro Ebene zwei Vergleiche und eine Vertauschung) die Heapeigenschaft wiederhergestellt. Dies kostet einen Aufwand von O(ld n).

11 Entfernen des minimalen Elementes aus einem Heap und Wiederherstellung der Heapeigenschaft
Algorithmus deletemin (h) {lösche das minimale Element aus dem Heap h und gib es aus} Entnimm der Wurzel ihren Eintrag und gib ihn als Minimum aus. Nimm den Eintrag der letzten besetzten Position im Baum, lösche diese Position und setze ihren Eintrag in die Wurzel ein. Sei p die Wurzel, und seien q und r die Söhne der Wurzel Solange Söhne q oder r existieren und (m(p) > m(q) oder m(p) > m(r) ) gilt: {vertausche p mit dem Sohn mit dem kleineren Eintrag. Setze q und r auf die neuen Söhne dieses Knotens.}

12 Einfügen in einen Heap:
Algorithmus insert (h, e) {füge einen neuen Knoten q mit Eintrag e in den Heap h ein} erzeuge einen neuen Knoten q mit Eintrag e; füge q auf der ersten freien Position in der untersten Ebene ein; falls die unterste Ebene voll besetzt ist, beginne eine neue Ebene. Sei p der Vater von e; Solange p existiert und m(p) > e gilt: {Vertausche die Einträge in p und q, setze q auf p und p auf den Vater von p.}

13 Sei n die Zahl der Knoten des Heap.
Aufwand für das Entfernen des minimalen Elementes: O(log n). für das Einfügen eines neuen Knotens: O(log n). Denn ein Heap hat Höhe [log2 (n+1)]+ -1 für das Aufbauen des Heap durch wiederholtes Einfügen von Knoten: O(n log n). Wir haben gesehen, dass man einen Heap in O(n) Schritten aufbauen kann, wenn diese Elemente von Anfang an alle bekannt sind.

14 5.6 Partitionen von Mengen mit Find und Merge
Sei S eine Menge, P eine Partition von S, also eine Menge von Teilmengen von S, die paarweise disjunkt sind und zusammen die Menge S ergeben. (Zwei Elemente von S heißen äquivalent, wenn sie zur gleichen Komponente gehören.) Wir wollen: find: bei Eingabe eines Elementes die Komponente finden, zu der es gehört, merge: bei Eingabe (der Namen oder von Elementen) zweier Komponenten diese verschmelzen.

15 Datentyp, algebraisch spezifiziert:
algebra partition sorts   partition, compname, elem ops     empty :   partition   addcomp: partition  compname  elem  partition   merge : partition  compname  compname  partition   find : partition  elem  compname sets    compname=Menge von Komponentennamen (aus einer Menge CN) partition={(ci ,Si) | 1  i  n, ci Komponentenname, Si Teilmenge von elem,                   ij  ci  cj und Si, Sj elementfremd.}

16 functions empty bildet auf die leere Partition ab (dann ist auch die partitionierte Menge S die leere Menge!) Sei p:={(c1,S1), ..., (cn,Sn)} eine Partition, S = Vereinigung der Si, und C= {c1, ..., cn}, C Komp. N. Sei a aus CN\C, x nicht in S enthalten.      addcomp(p,a,x) =p U {(a,{x})}         Seien a und b Elemente aus C mit den zugehörigen      Komponenten A und B, d ein Name aus CN\C oder d=a bzw. d=b. merge (p,a,b) = (p \ {(a,A), (b,B)}) U {(d,A U B)}     Sei x ein Element aus S.  find (p,x) = a mit (a,A) aus p und x aus A. end partition.

17 5.6.1 Implementierung mit Arrays
Fall elem := {1,..., n} und compname = {1,..., n}. Wir benutzen zwei Arrays der Größe n. Adjazenzmatrix (components): enthält die Komponentennamen und zu jedem ein Element der Komponente (wenn dies 0, dann Komponente leer). Inzidenzmatrix (elems): enthält die Elementnamen, zu jedem den Namen der zugehörigen Komponente und ein weiteres Element der Komponente (wenn dies 0, dann kein noch nicht erfasstes Element der Komponente). Man erhält für jede Komponente eine verkettete Liste!

18 Vertreter (0 unbenutzt)
Beispiel p = {(2,{1,2,4,5}), (3,{3}), (6,{6})}. S = {1,2,3,4,5,6}. components  compname Vertreter (0 unbenutzt) verlinkt mit 1 2 mit 1 3 mit 3 4 5 6 mit 6 elems compname nextelem 1 2 4 3 0 (entspricht NIL) 5 6

19 Algorithmus find: Zeit O(1).
Algorithmus merge (p, a, b); {verschmelze die Komponenten von a und b in der Partition p} Durchlaufe für a die zugehörige Liste in der Inzidenzmatrix elems und setze für jedes Element der Liste den zugehörigen Komponentennamen compname auf b; Sei j der Name des letzten Elements dieser Liste; Führe dann die folgenden Umbenennungen durch. elems[j].nextelem:= components[b].firstelem; components[b].firstelem:=components[a].firstelem; components[a].firstelem:=0; end merge.

20 Aufwand für merge: O(n).
Aufwand für n-1 merge-Anweisungen: Klar: O( Σi=1…n i) = O(n²). Kann verbessert werden zu: O(n log n): Schlage immer die kleinere Komponente zur größeren! Dann immer eine (mindestens) Verdoppelung der Größe der Komponente eines Elements. Amortisierte worst-case Laufzeit von merge: O(log n).

21 5.6.2 Implementierung mit Bäumen
Wir benutzen: ein Array mit den Elementen und für jedes Element einen Knoten in einem Baum, sowie zu jeder Komponente eine Baumstruktur. Name einer Komponente: das Element in der Wurzel des Baums.

22 Implementation mit Bäumen
elems 1 -> 1 2 -> 2 3 -> 3 -> K3 -> 3 4 -> 4 -> 1 -> K2 -> 1 5 -> 5 6 -> 6 -> K6 -> 6 Hier wird jede Komponente durch einen Baum dargestellt, dessen Knoten die Elemente der Komponente sind. Dann ist elems ein array[1..n]  von diesen Bäumen. Die Verweise führen von den Söhnen zum Vater, die Namen der Komponenten sind Zeiger auf die Wurzeln der zugehörigen Bäume: Der Wurzelknoten enthält neben dem Namen der Komponente noch deren Größe.

23 Algorithmen find(p,x): starte im Array bei x und laufe zur Wurzel des Baums der Komponente. Aufwand: O( Höhe des Baums). merge(p,a,b): Mache a zum Sohn von b oder umgekehrt. Aufwand: O(1). Höhenbeschränkung zu erreichen durch: Wurzel der kleineren Komponente wird Sohn der Wurzel der größeren Komponente. Dann: Höhe  log n.

24 Pfadkompression Noch eine Verbesserung: Pfadkompression:
bei find(p,x) mache alle Knoten auf dem Pfad von x zur Wurzel zu Söhnen der Wurzel. Dann n find-Operationen beinahe in linearer Zeit: O(n • G(n)) Dabei wächst G sehr langsam: G(n)  5 für n 

25 Kapitel 6: Suchbäume und weitere Sortierverfahren
6.1   Binäre Bäume Die Klasse BinTree mit Traversierungsmethoden 6.2   Suchbäume 6.2.1   AVL Bäume 6.3   HeapSort und BucketSort 6.3.1   HeapSort 6.3.2   BucketSort

26 6.1 Binäre Bäume Bäume als Graphen:
Gerichtete Graphen: bestehen aus Knoten (Knotenmenge V) und Kanten (Kantenmenge E:Teilmenge von V V). Bäume: spezielle gerichtete Graphen: (1) haben einen ausgezeichneten Knoten, die Wurzel w, (2) haben die Eigenschaft, dass zu jedem Knoten k des Baumes genau eine Kantenfolge (ein Pfad) existiert, die w mit k verbindet. (3) haben keine Zykeln, d.h. es gibt keine Kantenfolge, die den gleichen Knoten mit sich verbindet. Bäume: andere Charakterisierung: zusammenhängende gerichtete Graphen mit #(V) = #(E) + 1.

27 Graphische Darstellung
Bäume werden graphisch i.d.R. so dargestellt, dass die Wurzel die oberste Position einnimmt und Kanten von oben nach unten gerichtet sind (Hierarchie). Knoten, von denen keine weiteren Kanten ausgehen, heißen äußere Knoten oder Blätter. Die anderen Knoten heißen innere Knoten.

28 Beispiel: Binärbaum zum Ausdruck ((12/4)•2)
Hier: innere Knoten: Kreise Blätter: Rechtecke

29 Bezeichnungen Ist (k,k´) eine Kante in einem Baum, so heißt
k´ Kind (Sohn, Nachfolger) von k und k Vorgänger von k´. Nachfolgerrelation eines Baumes: { (k, k´) | k innerer Knoten, k´ Nachfolger von k´} Vorgängerfunktion eines Baumes: partielle Funktion, die jedem Knoten außer der Wurzel den Vorgänger zuordnet.

30 Binärbäume: Bäume, in denen jeder Knoten maximal zwei Nachfolger hat.
Direkte Definition von Binärbäumen: T ist genau dann ein Binärbaum (mit Knotenelementen aus M), wenn (i) T = < T1, x, T2 >  wobei T1, T2 Binärbäume und x aus M oder (ii) T = < > (leere Struktur, null)

31 Hier: Bei einem Binärbaum T = < T1, x, T2 > ist x die Wurzel des Baums. Knoten: die Wurzeln von Teilbäumen. Kanten: von der Wurzel x eines Teilbaumes T = < T1, x, T2 > zu den Wurzeln von T1 und T2. Wir identifizieren im Folgenden häufig einen Knoten mit dem entsprechenden Wert (Inhalt) x. Ein Knoteninhalt kann in einem Binärbaum mehrfach vorkommen. Eindeutige Identifizierung eines Knoten: durch Links-/Rechts-Entscheidungen von der Wurzel zu dem Knoten: Bitfolge. Diese Definition kann auf k-näre Bäume erweitert werden.

32 Pfade Pfad: Knotenfolge x0 , x1 , ... , xn , wobei jeweils xi+1 Nachfolger (Kind) von xi ist, d.h. (xi,xi+1) Kante. Länge eines Pfades = Zahl der Kanten, hier also n. Ein nur aus einem Knoten bestehender Pfad hat die Länge 0.

33 Die Höhe eines Baumes T ist die maximale Länge eines Pfades in T.
(Die Anzahl der Ebenen im Baum ist um eins größer als die Höhe des Baumes.) Ein Binärbaum heißt vollständig, wenn alle Ebenen bis auf die letzte vollständig besetzt sind. … links-vollständig, wenn er vollständig ist und die unterste Ebene von links nach rechts aufgefüllt ist. Auch diese Definitionen gelten für k-näre Bäume


Herunterladen ppt "Kapitel 5: Von Datenstrukturen zu Abstrakten Datentypen"

Ähnliche Präsentationen


Google-Anzeigen