Datenstrukturen

Werbung
Datenstrukturen
Mariano Zelke
Sommersemester 2012
Kapitel 3:
Elementare Datenstrukturen
Mariano Zelke
Datenstrukturen
2/18
Einfach verkettete Listen
Eine Zeiger-Implementierung von einfach verketteten Listen, also Listen
mit Vorwärtszeigern.
//Deklarationsdatei liste.h für einfach verkettete Listen.
enum boolean {False, True};
class liste{
private:
typedef struct Element {
int data;
Element *next;
};
//Kann auch anderer Datentyp sein
Element *head, *current;
public: liste( ) // Konstruktor
{ head = new Element; current = head; head->next = 0; }
Mariano Zelke
Datenstrukturen
3/18
Public: Die weiteren Operationen
I
I
I
I
I
I
I
I
I
void insert(int data); // Ein neues Element mit Wert data
// wird nach dem gegenwärtigen Element eingefügt. Der Wert von
// current ist unverändert.
void remove( ); // Das dem gegenwärtigen Element folgende
// Element wird entfernt. Der Wert von current ist unverändert.
void next( ); // Das nächste Element wird aufgesucht. Dazu
// wird current um eine Position nach rechts bewegt.
void movetofront( ); // current erhält den Wert head.
boolean end( ); // Zeigt current auf das letzte Element?
boolean empty( ); // Ist die Liste leer?
int read( ); // Gib das Feld data des nächsten Elements aus.
void write(int wert); // Überschreibe das Feld data des
// nächsten Elements mit der Zahl wert.
void search(int wert); // Suche rechts von der gegenwärtigen
// Zelle nach der ersten Zelle z mit Datenfeld wert. Der Zeiger
// current wird auf den Vorgänger von z zeigen.
}
Mariano Zelke
Datenstrukturen
4/18
Eigenschaften
I
Jede Liste besitzt stets eine leere Kopfzelle: liste() erzeugt diese
Zelle, nachfolgende Einfügungen können nur nach der Kopfzelle
durchgeführt werden.
I
Alle Operationen können leicht in konstanter Zeit O(1) unterstützt
werden, nur die Funktion search benötigt möglicherweise Zeit
proportional zur Länge der Liste.
The good and the bad
Die Größe der Liste ist proportional zur Anzahl der
gespeicherten Elemente: Die Liste passt sich der
darzustellenden Menge an.
Die Suche dauert viel zu lange.
Mariano Zelke
Datenstrukturen
5/18
Die Addition dünn besetzter Matrizen
A und B sind Matrizen mit n Zeilen und m Spalten.
Berechne die Summe C = A + B.
I
Das Programm
for (i=0 ; i < n; i++)
for (j=0 ; j < m; j++)
C[i,j] = A[i,j] + B[i,j];
bestimmt C in Zeit O(n · m).
I
Ziel: Bestimme C in Zeit O(a + b), wobei a und b die Anzahl der
von Null verschiedenen Einträge von A und B ist.
Mariano Zelke
Datenstrukturen
6/18
Listendarstellung von Matrizen
I
Stelle A und B durch einfach verkettete Listen LA und LB in
Zeilenordnung dar.
Jedes Listenelement speichert neben dem Wert eines Eintrags auch
die Zeile und die Spalte des Eintrags.


0 9 0
Die Liste LA in Zeilenordnung für A =  7 0 5  ist
0 0 6
*head
I
data:
*next
Mariano Zelke
data:
(1,2,9)
*next
data:
(2,1,7)
*next
data:
(2,3,5)
*next
Datenstrukturen
data:
(3,3,6)
*next
7/18
Der Algorithmus
(1) Beginne jeweils am Anfang der Listen LA und LB .
(2) Solange beide Listen nicht leer sind, wiederhole
(a) das gegenwärtige Listenelement von LA (bzw. LB ) habe die
Koordinaten (iA , jA ) (bzw. (iB , jB )).
(b) Wenn iA < iB (bzw. iA > iB ), dann füge das gegenwärtige
Listenelement von LA (bzw. LB ) in die Liste LC ein und gehe zum
nächsten Listenelement von LA (bzw. LB ).
(c) Wenn iA = iB und jA < jB (bzw. jA > jB ), dann füge das
gegenwärtige Listenelement von LA (bzw. LB ) in die Liste LC ein und
gehe zum nächsten Listenelement von LA (bzw. LB ).
(d) Wenn iA = iB und jA = jB , dann addiere die beiden Einträge und
füge die Summe in die Liste LC ein. Die Zeiger in beiden Listen
werden nach rechts bewegt.
(3) Wenn die Liste LA (bzw. LB ) leer ist, kann der Rest der Liste LB
(bzw. LA ) an die Liste LC angehängt werden.
Mariano Zelke
Datenstrukturen
8/18
Beispiel




0 4 0
0 0 0
A =  0 0 2 , B =  7 0 5 , Berechne C = A + B
1 0 0
0 0 0
*current
*head
Listendarstellung
von A :
data:
*next
data:
(1,2,4)
*next
data:
*next
data:
(2,1,7)
*next
data:
(2,3,5)
*next
*current
*head
Listendarstellung
von C :
data:
*next
Erzeugt in Schritt
Mariano Zelke
data:
(3,1,1)
*next
*current
*head
Listendarstellung
von B :
data:
(2,3,2)
*next
data:
(1,2,4)
*next
(2)(b)
data:
(2,1,7)
*next
data:
(2,3,7)
*next
data:
(3,1,1)
*next
(2)(c)
(2)(d)
(3)
Datenstrukturen
9/18
Deques, Stacks und Queues
I
Der abstrakte Datentyp Stack unterstützt die Operationen
I
I
I
Anwendung: nicht-rekursive Implementierung rekursiver
Programme, Druckerwarteschlange, pipe, etc.
Eine Queue modelliert das Konzept einer Warteschlange und
unterstützt die Operationen
I
I
I
push : Füge einen Schlüssel ein.
pop : Entferne den jüngsten Schlüssel.
enqueue : Füge einen Schlüssel ein.
dequeue: Entferne den ältesten Schlüssel.
Stacks und Queues werden von einem Deque (Double-ended queue)
verallgemeinert. Die folgenden Operationen werden unterstützt:
I
I
I
I
insertleft : Füge einen Schlüssel am linken Ende ein.
insertright : Füge einen Schlüssel am rechten Ende ein.
removeleft : Entferne den Schlüssel am linken Ende.
removeright : Entferne den Schlüssel am rechten Ende.
Alle drei Strukturen Stack, Queue und Deque unterstützen zusätzlich die
Operation empty, die jeweils auf Leerheit testet.
Mariano Zelke
Datenstrukturen
10/18
Die Implementierung eines Deque
I
I
I
Benutze ein an beiden Enden zusammengeschweißtes“ 1-dimen”
sionales Array. Der Inhalt des Deques wird jetzt entsprechend den
Operationen über den entstandenen Ring“ geschoben.
”
Benutze zwei Positionsvariable left und right: left steht jeweils vor
dem linken Ende und right jeweils auf dem rechten Ende.
Forderung: Die Zelle mit Index left ist stets leer und anfänglich ist
left = right.
Beispiel: Array der Länge 4:
nach
insertleft(1):
Anfangs:
nach
insertleft(2):
left right
1
right
nach
removeright():
2
1
right
nach
removeright():
left
left
2
left
right
left right
Mariano Zelke
Datenstrukturen
11/18
Eine Implementierung von Listen durch Arrays
Eine einfach verkettete Liste bestehe aus Zellen mit einem
Integer-Datenfeld und einem Vorwärtszeiger. Zu keinem Zeitpunkt möge
die Liste mehr als n Elemente besitzen.
I
Daten und Zeiger seien Arrays der Größe n.
I
Wenn ein Listenelement den Index i erhält“, dann ist Daten[i] der
”
Wert des Listenelements und Zeiger[i] der Index des rechten
Nachbarn.
Für die Speicherverwaltung benutze eine zusätzliche Datenstruktur
Frei, die die Menge der freien Indizes verwaltet.
I
Zu Anfang ist Frei = {0, . . . , n − 1}.
Mariano Zelke
Datenstrukturen
12/18
Beispiel
Die einfach verkettete Liste
*head
data:
*next
data:
4
*next
data:
9
*next
data:
3
*next
kann dargestellt werden durch
Daten
0 0 9 0 0 3 0 4
Zeiger
7 0 5 0 0 -1 0 2
Frei:
Mariano Zelke
{1,3,4,6}
Datenstrukturen
13/18
Eine Implementierung von Listen durch Arrays (cont.)
I
Wenn ein Element mit Wert w nach einem Element mit Index i
einzufügen ist:
Wenn Frei nicht-leer ist, dann entnimm einen freien Index j daraus
und setze Daten[j] := w , Zeiger[j] := Zeiger[i] und Zeiger[i] := j.
I
Wenn ein Element zu entfernen ist, dessen Vorgänger den Index i
besitzt:
Füge j := Zeiger[i] in die Datenstruktur Frei ein und setze
Zeiger[i] := Zeiger[j].
I
Welche Datenstruktur ist für Frei zu wählen? Jede Datenstruktur,
die es erlaubt, Elemente einzufügen und zu entfernen, tut’s:
Ein Stack, eine Queue oder ein Deque ist OK.
Mariano Zelke
Datenstrukturen
14/18
Beispiel
Füge Listenelement ein:
*head
data:
*next
data:
4
*next
data:
9
*next
Daten
0 0 9 0 8 3 0 4
Zeiger
7 0 4 0 5 -1 0 2
Frei:
Mariano Zelke
data:
8
*next
data:
3
*next
{1,3,4,6}
Datenstrukturen
15/18
Beispiel
Entferne Listenelement:
*head
data:
*next
data:
4
*next
data:
9
*next
Daten
0 0 9 0 8 3 0 4
Zeiger
7 0 4 0 5 -1 0 4
Frei:
Mariano Zelke
data:
8
*next
data:
3
*next
{1,3,6,2}
Datenstrukturen
15/18
Bäume
(Link)
Bäume: Hilfsmittel bei der hierarchischen Strukturierung von Daten.
I
Bäume als konzeptionelle Hilfsmittel für
I
I
I
I
von einem Benutzer angelegte Verzeichnisse,
für die Veranschaulichung rekursiver Programme,
...
Bäume als Datenstrukturen zur Unterstützung von Algorithmen:
I
I
I
I
Mariano Zelke
in der Auswertung von arithmetischen Ausdrücken,
in der Syntaxerkennung mit Hilfe von Syntaxbäumen
als Datenstrukturen zur Lösung von Suchproblemen,
...
Datenstrukturen
16/18
Was ist ein Baum?
Ein Baum T wird durch eine Knotenmenge V und eine Kantenmenge
E ⊆ V × V dargestellt.
Eine gerichtete Kante (i, j) führt von i nach j.
Wann ist T ein Baum?
I
T muss genau einen Knoten r besitzen, in den keine Kante
hineinführt. r heißt die Wurzel von T .
I
In jeden Knoten darf höchstens eine Kante hineinführen,
I
und jeder Knoten muss von der Wurzel aus erreichbar sein.
Eine disjunkte Vereinigung von Bäumen heißt Wald.
Mariano Zelke
Datenstrukturen
17/18
Zentrale Begriffe
I
Wenn (v , w ) eine Kante ist, dann nennen wir v den Vater von w
und sagen, dass w ein Kind von v ist.
I
I
Ausgangsgrad (v ) ist die Anzahl der Kinder von v .
I
I
I
Einen Knoten b mit Ausgangsgrad (b) = 0 bezeichnen wir als Blatt.
T heißt k-när, falls der Ausgangsgrad aller Knoten höchstens k ist.
Für k = 2 sprechen wir von binären Bäumen.
Ein Weg von v nach w ist eine Folge (v0 , . . . , vm ) von Knoten mit
v0 = v , vm = w und (vi , vi+1 ) ∈ E für alle i (0 ≤ i < m).
I
I
I
I
I
Die anderen Kinder von v heißen Geschwister von w .
v ist ein Vorfahre von w und w ein Nachfahre von v .
Die Länge des Weges ist m, die Anzahl der Kanten des Weges.
Tiefe(v ) ist die Länge des (eindeutigen) Weges von r nach v .
Höhe(v ) ist die Länge des längsten Weges von v zu einem Blatt.
T heißt geordnet, falls für jeden Knoten eine Reihenfolge der Kinder
vorliegt.
Mariano Zelke
Datenstrukturen
18/18
Herunterladen