Algorithmen und Datenstrukturen 186 8 Elementare Datenstrukturen In diesem und dem folgenden Kapitel werden grundlegende Techniken der Darstellung und Manipulation dynamischer Mengen auf Computern vorgestellt. Dynamische Mengen sind Mengen, die sich zeitlich ändern können durch Einfügen und Entfernen von Elementen oder durch andere Operationen. In einer typischen Implementierung wird ein Element repräsentiert durch ein Objekt mit mehreren Komponenten. Auf Objekte kann zugegriffen werden, sobald ein Zeiger auf sie vorhanden ist. Bei einigen dynamischen Mengen besteht eines der Objektkomponenten aus einem Schlüssel. Sind die Schlüssel paarweise verschieden, so kann die Menge als Menge von Schlüsseln aufgefaßt werden. 8 Elementare Datenstrukturen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 187 Man teilt Operationen auf dyamischen Mengen ein in Anfragen (queries) und Modifikationen. Typische Operationen sind: SEARCH(S, k): Anfrage, welche bei gegebener Menge S und Schlüsselwert k einen Zeiger x auf ein Element aus S liefert mit key[x] = k sofern vorhanden, andernfalls den Sonderwert NIL. MINIMUM(S): Anfrage an vollständig geordnete Menge, welche einen Zeiger auf das Element aus S mit kleinstem Schlüssel liefert. MAXIMUM(S): analog zu MINIMUM, größter Schlüssel SUCCESSOR(S, x): Anfrage, welche zum Element x der vollständig geordneten Menge S einen Zeiger auf das nächstgrößere Element in S liefert oder, falls x das größte Element in S ist, NIL zurückliefert. PREDECESSOR(S, x): analog zu SUCCESSOR, nächstkleineres Element 8 Elementare Datenstrukturen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 188 INSERT(S, x): Modifikation, welche der Menge S das Element, auf welches x zeigt, hinzufügt. (Hierbei wird angenommen, dass für die Implementierung der dynamischen Menge erforderliche Felder bereits initialisiert sind.) DELETE(S, x): Modifikation, welche das Element, auf welches x zeigt, aus der Menge S entfernt. (Verwendet Zeiger auf Element, keinen Schlüsselwert) 8 Elementare Datenstrukturen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 189 8.1 Stacks und Queues Stacks (Stapel, Keller) und Queues (Schlangen) sind dynamische Mengen, bei denen das nächste entfernbare Element vorgegeben ist. Beim Stack kann nur das zuletzt eingefügte Element entfernt werden. Man bezeichnet dies als LIFO-Regel (last-in, first-out). Bei der Queue wird stets das Element als nächstes entfernt, welches bereits am längsten der Menge angehört (FIFO-Regel, first-in, first-out). In diesem Abschnitt stellen wir Implementierungen von Stacks und Queues vor, welche auf Feldern beruhen. 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 190 8.1.1 Stacks Stack aus höchstens n Elementen dargestellt durch Feld S[1.. n] Terminologie: INSERT-Operation heißt PUSH, DELETE heißt POP. Attribut top[s]: Index des zuletzt eingefügten Elementes Stack besteht aus Teilfeld S[1.. top[s]]. S[1] unterstes, S[top[S]] oberstes Element Gilt top[s] = 0, so ist der Stack leer. Operation STACK-EMPTY testet auf leeren Stack. POP-Operation auf leeren Stack führt zu Unterlauf-Fehler, falls top[s] > n tritt (bei PUSH) Überlauf auf. Alle Operationen vom Aufwand O(1). 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 191 STACK-EMPTY(S) 1 if top[s] = 0 2 then return TRUE 3 else return FALSE PUSH(S, x) 1 top[s] top[s] + 1 2 S[top[S]] x POP(S) 1 if STACK-EMPTY(S) 2 then error Unterlauf 3 else top[s] top[s] 1 4 return S[top[S] + 1] 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 192 1 2 3 4 5 6 7 S 15 6 2 9 Stack S top[s]=4 1 2 3 4 5 6 7 S 15 6 2 9 17 3 nach PUSH(S, 17), PUSH(S, 3) top[s]=6 1 2 3 4 5 6 7 S 15 6 2 9 17 3 nach POP(S) top[s]=5 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 193 8.1.2 Queues INSERT-Operation heißt ENQUEUE, DELETE-Operation heißt DEQUEUE. ENQUEUE fügt neues Element am Ende (tail) der Schlange an, DEQUEUE entfernt Element am Kopf (head) der Schlange. Implementierung einer Schlange mit n 1 Elementen durch Feld Q[1.. n] Attribut head[q]: Index des Schlangenkopfes Attribut tail[q]: Index des Schlangenendes Elemente liegen an den Positionen head[q], head[q] + 1,..., tail[q] 1. Position 1 folgt unmittelbar auf Position n (kreisförmige Anordnung, wrap-around ) 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 194 Anfangszustand: head[q] = tail[q] = 1 Schlange leer, falls head[q] = tail[q]. DEQUEUE angewandt auf leere Schlange führt zu Unterlauf-Fehler. Schlange voll, falls head[q] = tail[q] + 1. ENQUEUE angewandt auf volle Schlange führt zu Überlauf-Fehler. (Prüfung auf Fehler in folgendem Pseudocode weggelassen.) Alle Operationen besitzen Aufwand O(1). 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 195 ENQUEUE(Q, x) 1 Q[tail[Q]] x 2 if tail[q] = length[q] 3 then tail[q] 1 4 else tail[q] tail[q] + 1 DEQUEUE(Q) 1 x Q[head[Q]] 2 if head[q] = length[q] 3 then head[q] 1 4 else head[q] head[q] + 1 5 return x 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 196 Q 1 2 3 4 5 6 7 8 9 10 11 12 15 6 9 8 4 Schlange Q head[q]=7 tail[q]=12 Q 1 2 3 4 5 6 7 8 9 10 11 12 3 5 15 6 9 8 4 17 nach ENQUEUE(Q, 17), ENQUEUE(Q, 3), ENQUEUE(Q, 5) tail[q]=3 head[q]=7 Q 1 2 3 4 5 6 7 8 9 10 11 12 3 5 15 6 9 8 4 17 nach DEQUEUE(Q) tail[q]=3 head[q]=8 8.1 Stacks und Queues TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 197 8.2 Verkettete Listen Datenstruktur aus linear angeordneten Objekten; Ordnung nicht durch Feldindizes, sondern durch Verzeigerung gegeben. Unterstützt (wenn auch nicht effizient) alle Operationen aus Einleitung. Jedes Objekt enthält Schlüssel, evtl. weitere Nutzdaten, und eine (einfach verkettete Liste) oder zwei (doppelt verkettete Liste) Zeiger-Komponenten. Bei doppelt verketteter Liste L: Zeiger prev auf Vorgänger-Objekt, Zeiger next auf Nachfolger-Objekt. Ist next[x] = NIL, so hat x keinen Nachfolger, ist also letztes Element (am Ende der Liste). Ist prev[x] = NIL, so hat x keinen Vorgänger, ist also erstes Element (am Kopf der Liste). 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 198 Attribut head[l] zeigt auf erstes Element der Liste. Ist head[l] = NIL, so ist die Liste leer. Einfach verkettete Liste ohne prev-zeiger Sortierte Liste: Schlüsselfelder sortiert in linearer Anordnung der Objekte (aufsteigend von Kopf bis Ende) Kreisförmige Liste: prev-zeiger des Kopfes zeigt auf Ende, next-zeiger des Endes Zeigt auf Kopf. im weiteren: doppelt verkettete, unsortierte Listen. head[l] 9 16 4 1 Doppelt verkettete Liste mit dem Inhalt {1, 4, 9, 16}; Zeiger durch Pfeile dargestellt, prev-zeiger rosa, next-zeiger blau; /-Symbol bezeichnet NIL 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 199 8.2.1 Suchen in einer verketteten Liste Algorithmus LIST-SEARCH(L, k): bestimmt erstes Element in Liste L mit Schlüssel k und liefert Zeiger hierauf zurück (NIL, falls keines vorhanden). LIST-SEARCH(L, k) 1 x head[l] 2 while x NIL and key[x] k 3 do x next[x] 4 return x Beispiele: Anwendung von LIST-SEARCH(L, 4) auf obige Liste liefert Zeiger auf drittes Element. Anwendung von LIST-SEARCH(L, 7) hingegen liefert NIL. 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 200 8.2.2 Einfügen in eine verkettete Liste Algorithmus LIST-INSERT(L, x) setzt gegebenes Objekt x (Schlüssel-Komponente als definiert vorausgesetzt) an den Anfang der Liste. LIST-INSERT(L, x) 1 next[x] head[l] 2 if head[l] NIL 3 then prev[head[l]] x 4 head[l] x 5 prev[x] NIL Laufzeit: O(1). 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 25 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 25 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 25 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 25 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 201 Beispiel: Wirkungsweise von LIST-INSERT head[l] 9 16 4 1 25 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 202 8.2.3 Löschen aus einer verketteten Liste Algorithmus LIST-DELETE entfernt ein Element x aus Liste L bei gegebenem Zeiger auf x. Soll ein Element mit einem spezifischen Schlüssel gelöscht werden, so muß mit LIST-SEARCH zuvor ein Zeiger auf dieses Element beschafft werden. LIST-DELETE(L, x) 1 if prev[x] NIL 2 then next[prev[x]] next[x] 3 else head[l] next[x] 4 if next[x] NIL 5 then prev[next[x]] prev[x] Laufzeit: O(1). Löschen eines speziellen Schlüssels: Θ(n). 8.2 Verkettete Listen TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 203 8.3 Implementierung von Zeigern und Objekten In diesem Abschnitt betrachten wir Implementierungen verketteter Datenstrukturen ohne Verwendung eines expliziten Zeiger-Datentyps. (Manche Programmiersprachen besitzen keinen) 8.3.1 Mehrfelddarstellung von Objekten Sammlung von Objekten mit jeweils mehreren Komponenten dargestellt durch ein Feld pro Komponente. Beipiel nächste Folie 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 204 L 7 1 2 3 4 5 6 7 8 next key prev 3 4 5 / 1 2 2 16 7 5 9 / 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 205 8.3.2 Einfeld-Darstellung von Objekten Hier soll ein Objekt einen zusammenhängenden Speicherbereich belegen. Zeiger ist einfach Adresse der ersten zugehörigen Speicherzelle. Weitere Komponenten können durch Addieren von Offsets auf diesen Zeiger erreicht werden. Analoge Implementierung durch Felder. Im Beispiel nächste Folie: Feld A speichert die bisher betrachtete verkettete Liste. Jedes Objekt liegt in Teilfeld A[j.. k]. Einzelne Komponenten des Objekts erreichbar durch Offsets zwischen 0 und k j. Index j stellt Zeiger auf dieses Objekt dar. Vorteil: etwas flexibler, kann auch für heterogene Objekte angepasst werden. 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 206 L 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 A 4 7 13 1 / 4 16 4 19 9 13 / key next prev 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 207 8.3.3 Allokieren und Freisetzen von Objekten Bei Manipulationen an verketteten Listen wechseln Objekte ständig zwischen benutztem und unbenutztem Zustand. Über den zugehörigen Speicherplatz muß Buch geführt werden, damit Speicher für nicht länger genutzte Objekte wieder verfügbar wird. (Garbage Collection) Wir betrachten nun eine solche Verwaltung für eine durch Mehrfelddarstellung implementierte verkettete Liste aus homogenen Objekten. Grundoperationen: Allokieren (Anfordern) und Deallokieren (Freisetzen) von Objekten. 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 208 Felder in Mehrfelddarstellung mögen Länge m haben, in gegebenem Zustand mögen sie n m Objekte enthalten. D.h. n benutzte, m n freie (unbenutzte) Objekte. Diese können für später einzufügende Elemente verwendet werden. Halte freie Objekte in verketteter Liste: der Freiliste. Diese Verwendet nur das next-feld, welches die Nachfolger-Zeiger speichert. Der Kopf der Freiliste wird in der globalen Variablen free gespeichert. Bei lichtleerer Liste L kann die Speicherung von L mit der der Freiliste verschachtelt sein. Jedes Objekt entweder in L oder der Freiliste enthalten. 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 209 Freiliste ist ein Stack, d.h. das als nächstes allokierte Objekt wird das zuletzt freigesetzte sein. Wir verwenden eine Listenimplementierung der PUSH- und POP-Operationen ALLOCATE-OBJECT() 1 if free = NIL 2 then error Speicher erschöpft 3 else x free 4 free next[x] 5 return x 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 210 FREE-OBJECT(x) 1 next[x] free 2 free x Freiliste enthält anfangs alle n noch nicht allokierten Objekte. Ist die Freiliste erschöpft, erfolgt eine Fehlermeldung. Oft bedient eine einzige Freiliste mehrere andere Listen. Beide Operationen besitzen Laufzeit O(1). Können für jede Liste homogener Objekte angepasst werden, eines der Felder muss nur die Rolle des next-feldes übernehmen. 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 211 free 4 1 2 3 4 5 6 7 8 L 7 next / 3 / 8 2 key 4 1 16 prev 5 2 7 1 5 9 / 6 free 8 1 2 3 4 5 6 7 8 L 4 next key / 3 4 / 7 2 1 25 16 1 5 9 6 prev 5 2 / 7 / 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06
Algorithmen und Datenstrukturen 212 free 5 1 2 3 4 5 6 7 8 L 4 next / 3 / 7 8 1 5 key 4 1 25 9 prev 5 2 / 4 6 8.3 Implementierung von Zeigern und Objekten TU Bergakademie Freiberg, WS 2005/06