• Keine Ergebnisse gefunden

1.1 Elementare Datenstrukturen

N/A
N/A
Protected

Academic year: 2022

Aktie "1.1 Elementare Datenstrukturen"

Copied!
46
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Algorithmen & Datenstrukturen

1. Grundlagen

1.1 Elementare Datenstrukturen

(2)

Datenstrukturen

Gleichartige Daten (ob vom elementaren Datentyp oder strukturiert) werden meist bei absch¨atzbarer Datenmenge in einem Array gehalten, ansonsten wird die Gr¨oße der Datenstruktur dynamisch verwaltet.

Statische Datenobjekte Dynamische Datenobjekte

- Speicherbedarf muss zum Zeit- - Gr¨oße des Speicherbedarfs passt punkt der Programmerstellung sich zur Laufzeit an den tats¨ach-

bekannt sein lichen Bedarf an

- automatische Speicherverwal- - programmlaufabh¨angige Speicher-

tung verwaltung

- fortlaufender Speicherbereich - verketteter Speicherbereich

(3)

Listen (1)

• Lineare Liste, einfach verkettet

p_head

L i s t e n NIL

• Lineare Liste, doppelt verkettet

p_head p_tail

NIL

NIL

L i s

t e n

(4)

Listen (2)

• Ringliste, einfach verkettet

p_tail

p_head L i

s

t e

n

• Einige Anwendungen:

* geordnete Ablage von Daten,

* Speicherverwaltung: Verkettung der freien Speicherbereiche,

* schwach besetzte Matrizen.

(5)

Listen (3)

Listen sind bekannt aus EPR! Hier Wiederholung

• Eine Lineare Liste (LL) ist eine verkettete Folge von Elementen eines gegebenen Typs T.

• Einfach verkettete LL: Jedes Listenelement enth¨alt neben den Nutzdaten einen Verweis (Zeiger) auf seinen unmittelbaren Nach- folger.

• Doppelt verkettete LL: Jedes Listenelement enth¨alt zwei Verweise, einen auf seinen unmittelbaren Nachfolger und einen auf seinen unmittelbaren Vorg¨anger.

• Ein Zeiger auf N U LL zeigt das Ende der Liste an.

• Zirkulare Liste oder Ringliste: Jedes Listenelement hat einen Nach- folger, der Zeiger des letzten Elements zeigt wieder auf das erste Element.

(6)

Listen (4)

Aufbau einer einfach verketteten linearen Liste

NIL p_head

p_next

Length

p head: Listenanfang als Verweis auf das erste Listenelement

p next: Verweis auf den Nachfolger

Length: Anzahl der Elemente einer Liste

NIL: Ende der Liste, in C Verweis auf NULL.

(7)

Listen (5)

Elementare Operationen

Create: Liste erzeugen: Head setzen und das erste Listenelement eintragen.

Destroy: alle Elemente entfernen.

Search: Element suchen Insert: Element einf¨ugen Delete: Element l¨oschen Update: Element ersetzen

Size: Anzahl der Elemente

(8)

Listen (6)

Beispiel f¨ur eine Liste in C typedef struct data {

...

} data_t

typedef struct element { data_t data;

struct element * p_next;

} element_t;

typedef struct list { int length;

element_t * p_head;

...

element_t * p_search;

} list_t

(9)

Listen (7)

Beispiel: ein Element in eine Liste mit sortierten Elementen einf¨ugen

NIL p_head p_search

p_new

(10)

Listen (8)

void insert_element(liste_t * liste, eintrag_t * p_new) {

// falls Liste leer ...

// Einf¨ugestelle suchen ...

// falls vor dem ersten Element eingef¨ugt wird ...

// ansonsten bei p_search einf¨ugen

p_new->p_next = liste->p_search->p_next;

liste->p_search->p_next = p_new;

}

(11)

Listen (9)

Beispiel: ein Element aus einer Liste l¨oschen

NIL

p_head p_search p_del

(12)

Listen (10)

int delete_element(liste_t * liste, eintrag_t * p_del) {

// falls Liste leer - Fehler ...

// Position von p_del suchen

// falls nicht vorhanden - Fehler ...

// ansonsten p_del l¨oschen // falls p_del=p_head

...

// ansonsten, sei p_search der Vorg¨anger von p_del p_search->p_next = p_del->p_next;

free(p_del);

liste->length--;

return 0;

}

Die weiteren Funktion sind analog zu den obigen aufgebaut.

(13)

Listen (11)

Implementation einer Liste mit fester maximaler Gr¨oße (bounded implementation)

Liegt die maximale Gr¨oße der Liste fest, kann eine statische Allokie- rung vorteilhaft sein. Anstatt Zeiger auf das n¨achste Datenelement werden die belegten und freien Elemente in einer Indexliste gef¨uhrt.

2 4 3 10 5 7 -1 8 11 6 9 -1

L i s n e t

0 1 2 3 4 5 6 7 8 9 10 11

head freeHead

list[12]

(14)

Listen (12)

In C

typedef struct element { data_t data;

int next;

} element_t;

typedef struct list {

element_t list_el[12]; /* Array aus 12 Elementen */

int head = -1 /* Verweis auf Listenanfang */

int freeHead = 0 /* Verweis auf erstes freies Element */

...

} list_t

-1 steht f¨ur NIL,

head zeigt auf das erste Listenelement

freeHead zeigt auf das erste freie Listenelement.

(15)

Listen (13)

Vergleich

• Bounded-Singly-Linked-List mit Arrays:

* Mehraufwand durch Verweise

* einfache Sortierung m¨oglich

• Bounded- mit Unbounded-Singly-Linked-List:

* Begrenzte Anzahl der Elemente

* vermeidet Speicherfragmentierung

(16)

Listen (14)

Beispiel: Einf¨ugen eines Elements am Anfang der Liste.

2 4 3 10 5 7 -1 8 11 6 9 -1

L i s n e t

0 1 2 3 4 5 6 7 8 9 10 11

head freeHead

2 0 3 10 5 7 -1 8 11 6 9 -1

L i s n e t

0 1 2 3 4 5 6 7 8 9 10 11

head freeHead

5

if (freeHead != NIL) { new_el = freeHead;

freeHead = list[freeHead].next;

list[new_el].data = daten;

list[new_el].next = head;

head = new_el;

}

(17)

Listen (15)

Doppelt-verkettete Liste

Die doppelt-verkettete Liste ist eine Erweiterung der einfach-verketteten Liste, in der Verweise in beide Laufrichtungen gespeichert sind.

p_head p_tail

NIL

NIL

L i s

t e n

Datenstruktur in C

typedef struct element { data_t data;

struct element * prev;

struct element * next;

} element_t;

Alle Funktionen sind analog zur einfach verketteten Liste aufgebaut.

(18)

Listen (16)

Doppelt verkettete Liste mit fester Gr¨oße

2 4 3 10 5 7 -1 8 11 6 9 -1

L i s n e t

0 1 2 3 4 5 6 7 8 9 10 11

Head FreeHead

0 2 9 10 3

-1

Tail

Auch hier werden alle Funktionen analog zur einfach verketteten Liste mit fester maximaler Gr¨oße aufgebaut werden.

(19)

Stack (1)

• Ein Stack (Stapel, Kellerspeicher) ist ein abstrakter Datentyp, bei dem Elemente in der sogenannten LIFO = Last-In-First- Out-Reihenfolge abgelegt werden und wieder eingelesen werden k¨onnen.

• D.h. die Daten werden in der umgekehrten Reihenfolge gelesen wie geschrieben.

• Anwendung: geschachtelte Strukturen unbekannter Tiefe wie z.B.

bei der Auswertung arithmetischer Ausdr¨ucke f¨ur die Speicherung von Zwischenergebnissen.

• Auf diese Art werden die Daten bei Funktionsaufrufen auf den Stack-Speicher gelegt.

(20)

Stack (2)

Typische Stackoperationen:

push: legt das Element x auf den Stack s peek/top: liefert das zuletzt auf den Stack s

gelegte Element

pop: (liefert und) entfernt das zuletzt auf den Stack s gelegte Element

isEmpty: gibt an, ob der Stack leer ist size: gibt die Gr¨oße des Stacks

Ein Stack kann als Array oder lineare Liste implementiert werden.

(21)

Stack (3)

Beispiel: Berechnung von 5.1*(((91+28)*(4.3+6))+777) in umgekehrter polnischer Notation:

5.1 91 28 + 4.3 6 + * 777 + *

#include <stdio.h>

int main(void) push(pop()+pop());

{ push(pop()*pop());

stackinit(); push(777);

push(5.1); push(pop()+pop());

push(91); push(pop()*pop());

push(28); printf("%d\n",pop());

push(pop()+pop()); if (stackempty())

push(4.3); printf(" Stack is empty now\n");

push(6); return 0;

}

(22)

Queue (1)

Eine Queue (Warteschlange, Puffer) ist ein Datentyp, bei dem auf die Elemente nach dem FIFO = First-In-First-Out-Prinzip zugegriffen wird. Dies erm¨oglicht das Auslesen der Daten in der Reihenfolge ihrer Speicherung.

Head Tail

Length

10 4 9 6 1 2 5

Head

8 10 4 9 6 1 2 5

Tail

(23)

Queue (2)

Speicherung

Eine Queue kann als lineare Liste, Ringliste oder Array implementiert werden.

Struktur

Es m¨ussen 2 Indizes mitgef¨uhrt werden:

head, die Position zum Eintragen von Elementen, tail, die Position zum Entnehmen der Elemente.

4 9 6 1 2 5

head tail

next element

(24)

Queue (3)

Elementare Operationen

Create: Queue erzeugen

Destroy: Queue l¨oschen Insert(AtHead): Element einf¨ugen Remove(FromTail): Element l¨oschen IsEmpty: Test auf Eintr¨age IsFull: Test auf F¨ullung LengthOf: L¨ange der Queue Anwendung:

• Pufferung zwischen unterschiedlich leistungsf¨ahigen Datenquellen wie z.B. Druckerwarteschlangen

• pipes in Unix

• Ereignisverarbeitung

(25)

Queue (4)

Ringbuffer

Ist die maximale L¨ange bekannt, kann eine Queue auch als zirkula- res Array (Ringpuffer) implementiert werden.

Head

4 Tail

7

121 6

33

43 77 9

1

2

3

4

5

6 7

8

9 10 11

12

next

(26)

Queue (5)

Problem:

• Head und Tail wandern entgegen des Uhrzeigersinns.

• Die Queue ist voll oder leer bei (Head+1)%12=Tail

• Ausweg: Setze Head auf -1, wenn die Queue voll ist und Tail auf -1, wenn die Queue leer ist.

(27)

B¨ aume (1)

B¨aume in der Informatik sind verzweigte Datenstrukturen.

Wurzel

Blätter

• Sie bestehen aus Knoten, die hierarchisch angeordnet sind und

¨

uber Kanten verbunden sind.

(28)

B¨ aume (2)

• Sie enthalten keine geschlossenen Maschen, d.h. sie sind zyklenfrei.

• Es gibt genau einen Knoten, der keinen Vorg¨anger (Eltern-Knoten, Parent-node) hat, die Wurzel (root) oder der oberste Knoten.

• Die untersten oder ¨außeren Knoten, an denen keine weiteren Kno- ten h¨angen, heißen Bl¨atter (leaves).

• Die direkten Nachfolger eines Knotens heißen Kinder (Children).

• Jeder Knoten, der kein Blatt ist, hat einen Teilbaum.

• Die Entfernung von der Wurzel ist die Stufe eines Knotens.

• Die maximale Anzahl der Teilbaumzeiger im Knoten ist der Grad (degree) eines Baums.

• Jeder Knoten ist von der Wurzel aus auf genau einem Weg zu erreichen.

(29)

B¨ aume (3)

Die Knoten eines Baumes sind meistens nach einer bestimmten Ord- nungsregel angeordnet.

Wurzel 44

67 39

20

03 25

42 60 75

59 65 72 89

44

Blätter Vorfahren von 03 und 25 67 Parent von 60 und 75

60 75 Childern von 67 Nachfahren von 67 Geschwister

(30)

B¨ aume (4)

Jeder Knoten eines Baums besteht aus einem Datenteil und zwei oder mehr Zeigern auf andere Knoten.

Bedeutung f¨ur die Informatik

• Hervorrangendes Hilfsmittel, sortierte Daten so abzuspeichern, dass jedes Datenelement m¨oglichst schnell auffindbar ist.

• Hierarchische Strukturen wie z.B. Verzeichnisstrukturen bei Be- triebssystemen.

• Repr¨asentation mathematischer Formeln, z.B. arithmetische Aus- dr¨ucke

• ...

(31)

B¨ aume (5)

Bin¨are B¨aume

• Jeder Knoten hat h¨ochstens zwei Teilbaumzeiger, d.h. er ist vom Grad 2.

• Er ist entweder leer oder besteht aus der Wurzel und zwei bin¨aren B¨aumen (rekursive Definition).

• Jeder Knoten enth¨alt Daten und 2 Verweise auf die Unterb¨aume.

Besitzt ein Knoten links oder/und rechts kein Child-Element, wird der entsprechende Zeiger auf NULL gesetzt.

• Eine einfach verkettete lineare Liste ist ein nicht-ausgeglichener bin¨arer Baum.

(32)

B¨ aume (6)

(33)

B¨ aume (7)

Geordnete B¨aume

• Jedes Datenelement enth¨alt einen Ordnungsbegriff, den Schl¨ussel, nach dessen Wert sortiert wird.

• Jeder linke Teilbaum eines Knoten enth¨alt nur Knoten mit kleine- ren Schl¨usselw¨ortern, jeder rechte Teilbaum nur Knoten mit gr¨oße- ren Schl¨usselw¨ortern (siehe Suchb¨aume).

B¨aume ausgleichen

• Ein ausgeglichener Baum ist ein Baum, der so flach wie m¨oglich ist, d.h. alle Bl¨atter liegen auf einer Ebene bzw. die vorletzte Ebene ist voll belegt.

• Ausgeglichene B¨aume werden oft verwendet, wenn Elemente ge- sucht werden sollen (schw¨achere Definitionen siehe AVL-B¨aumen).

(34)

Durchlaufen von B¨ aumen (1)

Methoden zum Durchlaufen (Traversing) von B¨aumen

09 53

05

11

15 79

20

Im Gegensatz zu den bisherigen Datenstrukturen ist das Durchlaufen von B¨aumen nicht eindeutig.

(35)

Durchlaufen von B¨ aumen (2)

Meistens werden 4 Typen der Bewegung verwendet:

• Preorder Traversing

1. Besuchen des Wurzelknotens

2. Preorder Traversing des linken Teilbaums 3. Preorder Traversing des rechten Teilbaums

09 53

05

11

15 79

20

(36)

Durchlaufen von B¨ aumen (3)

• Postorder Traversing (siehe Ausdrucksb¨aume) 1. Postorder Traversing des linken Teilbaums 2. Postorder Traversing des rechten Teilbaums 3. Besuchen des Wurzelknotens

09 53

05

11

15 79

20

(37)

Durchlaufen von B¨ aumen (4)

• Inorder Traversing

1. Inorder Traversing des linken Teilbaums 2. Besuchen des Wurzelknotens

3. Inorder Traversing des rechten Teilbaums

09 53

05

11

15 79

20

Die Daten in einem bin¨aren Suchbaum sind in Inorder-Reihenfolge korrekt sortiert.

(38)

Durchlaufen von B¨ aumen (5)

• Level-Order Traversing

1. Besuchen des Wurzelknotens

2. Durchlaufen jeder weitere Ebene von links nach rechts

09 53

05

11

15 79

20

(39)

Suchb¨ aume (1)

• Jeder Knoten eines Baumes enth¨alt ein Datenelement. Dieses un- terteilt sich normalerweise in ein Schl¨usselelement (Key), nachdem die Knoten sortiert sind, und eine Datenstruktur.

• Beispiel f¨ur eine Struktur eines Knotens in einem bin¨aren Such- baum in C

typedef struct node_b { struct node_b * p_left;

struct node_b * p_right;

int key;

data_t data;

} node_t;

Oft werden weitere Daten hinzugef¨ugt, wie z.B. ein Zeiger auf den Elternknoten oder die Tiefe der Teilb¨aume (siehe sp¨ater AVL- B¨aume).

(40)

Suchb¨ aume (2)

67 39

20

03 25

42 60 75

59 65 72 89

44

(41)

Suchb¨ aume (3)

• Inorder Traversing (rekursiv: linker Teilbaum, ausgeben, rechter Teilbaum).

67 39

20

03 25

42 60 75

59 65 72 89

44

67 39

03 20 25 42 44 59 60 65 72 75 89

(42)

Ausdrucksbaum (1)

Aufbau eines Ausdrucksbaums zur Verarbeitung von Ausdr¨ucken:

• Die als Child-Elemente jedes Knotens vorhandenen Teilb¨aume bil- den die Operanden des im Parent-Knoten gespeicherten Opera- tors.

• Die Operanden k¨onnen Terminale oder selbst Ausdr¨ucke sein.

• Ausdr¨ucke werden zu Teilb¨aumen expandiert

• Terminale stehen in den Blatt-Knoten.

(43)

Ausdrucksbaum (2)

Beispiel: ((74-10)/32) x (23+17)

A B

-

10

32 17

x

23

74

A x B

/ +

C

A=C/32 B=23+17

C=74-10

(44)

Ausdrucksbaum (3)

/ +

-

10

32 17

x

23

74

Postfix-Ausdruck: 74 10 - 32 / 23 17 + x

(45)

Ausdrucksbaum (4)

Postfix-Ausdruck: 74 10 - 32 / 23 17 + x

Dieses ist genau die Notation, die wir zur Abarbeitung von Ausdr¨ucken beim Thema Stack behandelt haben.

auf Stack 74 auf Stack 10

vom Stack 10 vom Stack 74 - anwenden auf Stack 64

auf Stack 32 vom Stack 32 vom Stack 64 / anwenden auf Stack 2

auf Stack 23 auf Stack 17

vom Stack 17 vom Stack 23 + anwenden auf Stack 40

vom Stack 40 vom Stack 2 x anwenden auf Stack 80

74 10

64

64 32

2

23 17

40

80

80

2 2

Achtung, bei den Operationen - und / muss auf die Reihenfolge der Operanden geachtet werden.

(46)

Operationen f¨ ur B¨ aume

• Traversierung eines Baums.

• Suchen eines Elements.

• Ver¨andern eines Knotens.

• Einf¨ugen eines neuen Knotens.

• L¨oschen eines Elements.

• Ausgleichen eines Baums durch “Rotation”.

Einzelne Algorithmen f¨ur diese Operationen wie ein Suchalgorithmus k¨onnen iterativ (Wiederhol-Schleife) oder rekursiv (sich selbst aufru- fende Funktion) durchgef¨uhrt werden.

Weiteres zu B¨aumen im Kapitel “Suchen”.

Referenzen

ÄHNLICHE DOKUMENTE

In einem Stack wird jeweils das erste Element gel¨oscht — ¨uber die Liste muss also nicht iteriert werden. Matteo nennt die Schritte f¨ur

Das Programm soll anschließend mit einer for-Schleife in diesem Feld nach dem Wert 0.125 suchen und den Index mit printf ausgeben.. 2 Aufgabe 2: Primzahlen mit dem Sieb

Die Anzahl der Personen im Kreis wird mit scanf eingelesen und dann dementsprechend eine Liste erzeugt. Diese soll

• Alternativ kann die Methode insert der Klasse SortedList so geschrieben werden, dass sie die Methode insert der Klasse LinkedList nicht verwendet. • Führen Sie dies zur

 Problem: im voraus nicht bekannt, wie groß das Array sein soll.  Also: zunächst ganz kleines Array erzeugen, dann

 Falls Ereignis noch nicht nach Zeitraum s eingetreten, dann ist Wahrscheinlichkeit dafür, daß Ereignis bis Zeit s+t eintritt genauso hoch wie W.keit, daß Ereignis bis Zeit

Mengen k¨ onnen also wieder Mengen als Elemente haben, wie zum Beispiel die Potenzmenge einer Menge, oder sind M und N Mengen, so ist {M, N } wieder eine Menge (diesen Prozess nennt

Definition 1.1.1 Eine Aussage ist ein Objekt (mathematischer 9.. Ausdruck, sprachliches Gebilde), dem genau der Wahrheitswert wahr oder falsch zugeordnet werden kann!. Für