7. Dynamischer Speicher und Datenstrukturen

7. Dynamischer Speicher und Datenstrukturen Inhalt: 7.1 Dynamisch angeforderter Speicher 7.2 Datenstrukturen Die Folien basieren zum Teil auf einen ...
Author: Klemens Egger
41 downloads 0 Views 142KB Size
7. Dynamischer Speicher und Datenstrukturen

Inhalt: 7.1 Dynamisch angeforderter Speicher 7.2 Datenstrukturen

Die Folien basieren zum Teil auf einen Foliensatz von R. Großmann und T. Wiedemann

Peter Sobe

1

Dynamische Speicherstrukuren (1) Unter dynamischen Speicherstrukturen versteht man die Bildung neuer Speicherstrukturen auf der Basis von Standarddatentypen und deklarierten Strukturen zur Laufzeit. Es wird Speicherplatz zur Laufzeit der Halde (Heap) entnommen, damit ein neues Objekt initialisiert und mit bereits vorhandenen Objekten in der Speicherstruktur verbunden. Ebenso können auch zur Laufzeit Objekte aus der Speicherstruktur herausgelöst werden und deren Speicherplatz wieder freigegeben werden. Im Gegensatz werden die bisher benutzen Variablen und Felder als statische Speicherstrukturen angesehen. Die Sichtbarkeit und der belegte Speicherplatz wird durch ihre Deklaration im globalen Kontext oder innerhalb Funktionen bestimmt.

Peter Sobe

2

Dynamische Speicherstrukuren (2) Zur Realisierung solcher dynamischer Strukturen werden zwei Technologien benötigt: 1. Die dynamische Speicherallokation und –freigabe im Heap und 2. Die Verwendung der Zeigertechnik zum dynamischen Herstellen und Lösen von Verbindungen von Objekten und der Datenstruktur Die interne Organisation der Daten (also wo welches Element im Speicher steht und wie es mit den anderen Elementen in Beziehung gesetzt wird) wird durch Datenstrukturen beschrieben. Peter Sobe

3

Dynamische Speicherstrukturen (3) Die dynamische Speicherallokation und –freigabe im Heap wird in C mit den Funktionen void *malloc(size_t size); // gibt die Anfangsadr. des // allokierten Bereichs zurück void free(void *addresse); durchgeführt, die in zu finden sind. Es werden size Bytes auf dem Heap allokiert und die Adresse dieses Speicherbereiches zurückgegeben. Ist der Rückkehrwert NULL, ist nicht genügend Speicher vorhanden. Ist die genaue Bytezahl bekannt, kann diese direkt als Argument übergeben werden, sonst empfiehlt sich die Verwendung von sizeof(x), die die notwendige Bytezahl für das Objekt x ermittelt. Bei der Speicherfreigabe mit free ist nur die Heapadresse des Speicherbereichs anzugeben. Peter Sobe

4

Dynamisches Feld (1) Bei manchen Anwendungen ist die Anzahl der zu verarbeitenden Elemente nicht bekannt und kann je nach Eingabewerten stark variieren. Felder mit einer maximalen Anzahl von Elementen sind dann ungünstig. Lösung: Ein dynamisches Feld, das sich mit Mitteln der dynamischen Speicherverwaltung (malloc, realloc, free) der benötigten Anzahl von Elementen anpasst. Zustand, nach Einfügen von 3 weiteren Elementen Startzustand, mit 3 Elementen als Reserve Elemente

Zustand, nach Einfügen von 2 Elementen

Elemente

Elemente

Zustand, nach Austragen von 4 Elementen Elemente

Peter Sobe

5

Dynamisches Feld (2) Konstanten und Variablen zur Verwaltung: #define N_START 3 #define N_DELTA 5 unsigned int n_allocated=0, n_elements=0;

Anfang:  Allokieren von N_START Elementen  Setzen von n_allocated = N_START feld = (elem_typ*) malloc(sizeof(elem_typ)* N_START); if (feld==NULL) { /* Fehlerbehandlung */} n_allocated = N_START;

Peter Sobe

6

Dynamisches Feld (3) Einfügen eines neuen Elements am Ende:  Prüfen, ob ausreichend Speicher allokiert ist  ggf. dynamisches Vergrößern des Feldes und Anpassen der Variable n_allocated  Einfügen des Elements in feld[n_elements]  Erhöhen von n_elements um 1 if (n_elements+1 > n_allocated) { feld = realloc(feld, sizeof(element_typ) * (n_allocated + N_DELTA)); if (feld==NULL) { /* Fehlerbehandlung */ } n_allocated = n_allocated + N_DELTA; } feld[n_elements] = neues_element; n_elements = n_elements+1;

Peter Sobe

7

Dynamisches Feld (4) Ausgliedern eines Elements an Position p (0≤pnext; pos->next = neu; }

pos->n pos

o

o neu

o neu->n = pos->n

Voraussetzung: Die Liste hat bereits Elemente. Peter Sobe

16

Ausketten eines Listenelementes (dequeue) void dequeue(list_elem_t *pos) { // pos zeigt auf Element vor dem auszukettenden Element list_elem_t *h; h=pos->next; pos->next=(pos->next)->next; free(h); }

pos->n = (pos->n)->n o

pos

o

o

pos->n free(h) Gesonderte Behandlung erforderlich, wenn erstes Element auszuketten ist. 17

Peter Sobe

Suchen eines Listenelementes (find) list_elem_t *find(int krit, list_elem_t *anker) { list_elem_t *a; a=anker; while (a!=NULL) { if ((a->i)==krit) return a; //Objekt a gefunden a=a->next; } return NULL; //nichts gefunden }

o a Peter Sobe

krit ?

o a=a->n

krit ? 18

Nichtlineare dynamische Datenstrukturen Eine nichtlinear verkettete Folge von Elementen (Objekten), die aus Standarddatentypen zusammengesetzt sind, nennt man nichtlineare dynamische Datenstruktur. Solche Strukturen sind vor allem: 

Bäume (Binärbäume, allg. Bäume) typisch für diese Strukturen ist die Anordnung als Hierarchie, d.h. zwischen den Elementen besteht noch eine Halbordnungsrelation



Graphen (kreisfreie Graphen, allgemeine Graphen) bei kreisfreien Graphen liegt noch eine Heterarchie als Struktur vor, während bei allg. Graphen nur netzartige Strukturen als allgemeinster Fall auftreten. Peter Sobe

19

Nichtlineare dynamische Datenstrukturen Viele Anwendungsalgorithmen basieren auf solchen nichtlinearen Strukturen. Bei netzartigen Graphstrukturen müssen diese Algorithmen mit erschöpfendem Durchsuchen aller Möglichkeiten arbeiten. Das erfordert programmseitig spezielle Hilfen (globale stacks). Bei Hierarchien (Bäume) reicht die einfache Rekursivität aus. Bei vielen Sonderfällen in Hierarchien nutzt man die s.g. Teile-undHerrsche-Algorithmen. Diese Algorithmen teilen den aktuellen Bearbeitungsraum in Teilbearbeitungsräume und wenden dann den gleichen Algorithmus rekursiv auf die Teile an (herrschen), solange bis eine weitere Teilung nicht mehr sinnvoll ist. Beispiel: Binärbäume Peter Sobe

20

Binärer Baum und Rekursion (1) Wurzel B0 Rechter Teilbaum B0R

Linker Teilbaum B0L

Binärbaum über Knotenmenge V B0= (W0, B0L ,B0R)  (W0, , ) Bx= (Wx, BxL ,BxR)  (Wx, , ) mit W0,B0 , Wx,Bx , ε V und  als leeres Element Indizes werden durch Aneinandereihung gebildet, z.B. x=0L → xL = 0LL, x=0R → xL = 0RL usw. Ein Baum ist entweder ein einzelner Knoten oder ein als Wurzel dienender Knoten, der mit einer Menge von Bäumen verbunden ist. (beim Binärbaum mit zwei Teilbäumen verbunden) 21

Peter Sobe

Binärer Baum und Rekursion (2) Verschiedene Strategien zum Traversieren des Baums Preorder: 1. Besuche die Wurzel des Baumes 2. Besuche den linken Teilbaum 3. Besuche den rechten Teilbaum

1 2 3

Peter Sobe

5 4

6

7

22

Binärer Baum und Rekursion (3) Verschiedene Strategien zum Traversieren des Baums Inorder (Symmetrische Strategie): 1. Besuche den linken Teilbaum 2. Besuche die Wurzel 3. Besuche den rechten Teilbaum

4 2 1

6 5

3

7

23

Peter Sobe

Binärer Baum und Rekursion (4) Verschiedene Strategien zum Traversieren des Baums Postorder: 1. Besuche den linken Teilbaum 2. Besuche den rechten Teilbaum 3. Besuche die Wurzel

7 6

3 1

Peter Sobe

2

4

5

24

Binärer Baum und Rekursion (5) Strategien zum Traversieren des Baums (Fortsetzung) Alle bisherigen Verfahren besuchen entweder tiefe Knoten oder links stehende Knoten zuerst. Bei Suchbäumen werden Lösungen u.U. erst spät gefunden. Level-Order-Traversierung: 

Besuche die Knoten “von links nach rechts“ innerhalb einer Ebene, danach die jeweils tiefere Ebene.



Diese Reihenfolge wird nicht durch Zeiger in innerhalb der Baumstruktur unterstützt



Diese Reihenfolge wird auch nicht durch Rekursion unterstützt 25

Peter Sobe

Binärer Baum mit sortierten Daten (1) Baum-Elemente: ID

Daten

Zeigerlinks

Zeigerrechts

Zeiger auf Baum-Wurzel:

15

8

4







12

20



17





23



NULL-Zeiger, wenn Nachfolge-Elemente nicht vorhanden: Peter Sobe

26

Binärer Baum mit sortierten Daten (2) Suchen eines Elements mit ID=x im sortierten Binärbaum: Knoten = Wurzel Aufsuchen Knoten:  falls ID==x dann gefunden, Ende  falls xID: Verfolge Zeiger-rechts Nach „Verfolge“ wird der jeweilige Knoten nach o.g. Regel besucht, solange bis  Knoten mit ID gefunden  oder ein Verfolgen auf den NULL-Zeiger trifft. Dann ist das gesuchte Element im Baum nicht vorhanden.

27

Peter Sobe

Bewertung Binärbaum Wurzel Beispiel:

15

8

20

4

2

17

12

6

10

16

14

23

19

21

26

im Bild

allgemein

Berechnung

Anzahl Ebenen

4

e

Konstruktionsparameter

Anzahl Elemente

15

n = 2e-1

Schritte zum Finden 4 (inkl. Zugriff eines Elements auf Wurzel) Peter Sobe

s=e

s =┌ log2 n ┐ 28