Algorithmen und Datenstrukturen 11 Sequen elle Datenstrukturen

Werbung
Algorithmen und Datenstrukturen Prof. Dr. Volker Blanz
Lehrstuhl für Medieninformatik Universität Siegen – Fakultät IV "
11 Sequen2elle Datenstrukturen Version: WS 14/15 Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.1 Algorithmen und Datenstrukturen 11 Sequentielle Datenstrukturen ...
Motivation:!
!
"❍ Speicherung/Verwaltung mehrerer Objekte gleichen Typs"
"
"❍ Problem bei Arrays: Feste Länge"
"
"❍ Ziel: Alternative Datenstrukturen und geeignete Grundoperationen wie"
" Suchen, Einfügen und Löschen"
"
Herausforderungen: Ausgiebige Nutzung von!
!
"❍ Zeigern"
"
"❍ dynamischen Datenstrukturen"
"
"Für beides wird ein solides Grundverständnis vorausgesetzt!"
"
Literatur:!
!
"❍ [Ernst] Kapitel 11.3"
"
"❍ [Gumm] Kapitel 4.5, 4.6, 4.7"
"
"❍ [Herold] Kapitel 8.2"
"
"❍ [Cormen] Kapitel 11 (der englischen Ausgabe, 2000)"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.2 Algorithmen und Datenstrukturen 11.1 Grundsätzliches
Ziel: Verwaltung von Datenobjekten eines Typs (bzw. Klasse) T"
"
Sequenzielle Datenstrukturen zeichnen sich aus durch"
"
"❍ Verwaltung von Objekten des Typs T in einer festen Reihenfolge"
"
"❍ Möglichkeit zur Teilung und Zusammenfügung"
"
"❍ Möglichkeit zum Einfügen neuer Elemente am Ende der Sequenz; zum"
" Einfügen an beliebiger Position muss die Sequenz zerlegt werden"
{t , t , t , t , t , t }→ {t , t , t }+ {t , t , t }
→ {t , t , t , t }+ {t , t , t }
→ {t , t , t , t , t , t , t }
1
2
3
4
5
6
1
2
3
4
1
2
3
neu
1
2
3
neu
5
6
4
4
5
5
6
6
"❍ Erlauben i.a. keinen direkten Zugriff auf ein beliebiges Element, sondern"
" nur ein schrittweises Durchlaufen"
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.3 Algorithmen und Datenstrukturen 11.1 Grundsätzliches …
Vergleich zu Arrays!
!
Voraussetzung dieses Vergleiches: Das vollständige Array wird genutzt (kein,,Platzvorrat“)"
"
Vergleich: Array und Liste"
"
"
!Operation/!
!Eigenschaft!
!
!Array
!
!
!
!Sequenzielle DS !
!
"Speicherumfang "
"Elementzugriff "
"Element einfügen
"
"
"
"
"Element löschen "
"
"
"
"
"statisch "
"
"
"direkte Adressierung "
"nur durch Neu-Allokation
"und Umkopieren"
"nur durch Neu-Allokation
"und Umkopieren"
"dynamisch"
"Durchlaufen der Sequenz"
"direkt möglich"
"direkt möglich"
"
"
Beachte: Selbst wenn das Array ausreichend viele Elemente besitzt, ist bei"
"Einfügen und Löschen i.a. ein Kopieren von Elementen notwendig"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.4 Algorithmen und Datenstrukturen 11.2 Verkettete Listen
Ziel: Umsetzung einer Sequenz von Objekten durch Verketten von Objekten"
"
Beschreibung:!
!
"❍ die Anzahl von Elementen in einer verketteten Liste ist dynamisch"
"
"❍ einfache Umsetzung von Einfüge- und Löschoperationen"
"
Vorteil:!
!
"❍ Eine solche dynamische Liste kann (im Gegensatz zu Arrays) während"
" der Laufzeit eines Programms ihre Länge ändern, ohne dass Elemente"
" umkopiert werden müssen"
"
Nachteil:!
"❍ Zugriffe auf die Elemente einer Liste benötigen i.a. mehr Zeit als bei"
" Arrays"
"
"❍ Listen gehören nicht zum C++-Sprachumfang; es gibt allerdings entsprechende"
" STL-Klassen (später)"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.5 Algorithmen und Datenstrukturen 11.2.1 Aufbau einer verketteten Liste
Listenelemente: Objekte, die miteinander über Zeiger verkettet werden"
"
Aufbau eines Listenelements:!
!
"❍ Datenteil: Objekt abstrakt vom Typ T oder Zeiger auf T
"❍ next-Zeiger auf das nächste Listenelement"
"
"class ListElem {
public:
// Konstruktor
ListElem(T∗ d) {
next = NULL;
data = d;
}
T∗
void
ListElem∗
Graphisches!
Symbol:
Daten/"
Objekt
getData(void)
{ return data; }
setNext(ListElem∗ n) { next = n; }
getNext(void)
{ return next; }
private:
T∗ data;
ListElem∗ next;
};
Next"
// Zeiger auf Datenobjekt
// Zeiger auf nächste Element
!
Hinweis: Wir gehen im Folgenden immer von Daten vom Typ T∗ aus!"
"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.6 Algorithmen und Datenstrukturen 11.2.1 Aufbau einer verketteten Liste ...
Objekte als Zeiger T∗ oder als Instanzen T verwenden?"
!
❍ T∗ : Instanzen müssen vom aufrufenden Programm verwaltet werden!"
!
•  Mit new und delete, also dynamisch verwaltet. "
Intern ist dies fester Speicherbereich, verwaltet von C."
!
•  Explizit mit einem Array als statischem Speichervorrat, in dem alle Inhalte in beliebiger Reihenfolge
liegen und das separat verwaltet wird. Neue Inhalte werden an irgendeine freie Stelle geschrieben.
Man muss darüber Buch führen, welche Speicherstellen (Array-Elemente) belegt und welche frei sind.
Vorteil: komplette Kontrolle, sehr effizient."
Array
Daten/"
Objekt
Next"
Daten/"
Objekt
Next"
!
!
!
!
!
!
!
!
!
Daten/"
Objekt
leer
Next"
Daten/!
Objekt
leer
Next"
❍ T : Ggf. aufwändiges Kopieren bei ListElem::ListElem() oder ListElem::getData()
"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.7 Algorithmen und Datenstrukturen 11.2.1 Aufbau einer verketteten Liste ...
Listenaufbau: Aneinanderfügen von Listenelementen über den next Zeiger"
"
next-Zeiger:!
!
"❍ verweist auf das nächste Listenelement der Sequenz"
"
"❍ das Listenende wird vereinbarungsgemäß durch next == NULL gekennzeichnet"
"
first-Zeiger:!
!
"❍ verweist auf erstes Listenelement und repräsentiert gesamte Liste"
"
"❍ wird separat in einer Klasse List gespeichert (s.u.)"
"
Beispiel einer Liste mit ihren Verkettungen"
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.8 Algorithmen und Datenstrukturen 11.2.2 Arbeiten in Listen
Navigation in Listen!
!
Navigation in (bzw.,,Durchlaufen“ von) Listen erfolgt durch sequentiellen Zugriff"
auf das jeweils nächste Listenelement"
"
Beispiel-Code: Unter der Annahme, dass die Liste bereits initialisiert ist, "
d.h. first verweist auf ein gültiges ListElem-Objekt"
"
"
"
ListElem∗ first = ...;
ListElem∗ curr;
curr = first;
// Zeiger auf das erste Listenelement
// Zeiger auf das aktuelle Listenelement
// Beginne beim ersten Listenelement
while( curr != NULL ) {
// Bearbeite das aktuelle Datenelement (z.B. durchsuchen oder ausdrucken)
curr->getData()->doSomething();
}
// gehe zum nächsten Listenelement
curr = curr->getNext();
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.9 Algorithmen und Datenstrukturen 11.2.2 Arbeiten in Listen …
Methoden einer Listenklasse!
!
Minimaler Funktionsumfang einer Liste ist"
"
"❍ Einfügen oder Löschen eines Elements am Anfang, innerhalb oder am"
" Ende der Liste (siehe unten)"
"
"❍ Suchen nach Datenelementen in der Liste, z.B. zum Löschen oder"
" Einfügen"
"
"❍ Rückgabe des ersten Listenelements, z.B. für die Navigation"
return first;"
"❍ Prüfen, ob die Liste leer ist"
return (first == NULL);"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.10 Algorithmen und Datenstrukturen 11.2.2 Arbeiten in Listen …
Methoden einer Listenklasse (Forts.)!
!
class List {
public:
List() { first = NULL; }
˜List();
// initial leere Liste
// Löschen aller ListElem
// Einfügemethoden
void prepend(T∗ d);
void append(T∗ d);
void insert(int i, T∗ d);
// Anfügen am Anfang
// Anfügen am Ende
// Anfügen an Position i
// Löschmethoden
void removeFirst(void);
void removeLast(void);
void remove(ListElem∗ elem);
// Löschen am Anfang
// Löschen am Ende
// Löschen von elem aus Liste
// sonstiges
bool
isEmpty(void);
ListElem∗ getFirst(void);
ListElem∗ find(T∗ d);
// prüfen ob Liste leer ist
// gibt Listenanfang zurück
// suche nach erstem Auftreten von ∗d
private:
ListElem∗ first;
}
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
// Zeiger auf erstes Listenelement
11.11 Algorithmen und Datenstrukturen 11.2.2 Arbeiten in Listen …
Suchen in einer Liste!
!
Ausgangslage: Eine (potentiell leere) Liste"
"
Gesucht: Erstes Listenelement, das ein gegebenes Datum enthält"
"
Implementierung der find-Methode der Klasse List"
"
"ListElem∗ List::find(T∗ d)
{
ListElem∗ result = first;
}
while ( result != NULL && ∗(result->getData()) != ∗d )
result = result->getNext();
return result;
Hinweise: ❍ Falls das Datum nicht in der Liste ist, wird NULL zurückgegeben"
"
"
"
❍ Logische Konjunktion „&&“ in der while-Bedingung wird von links her geprüft und bricht ab, sobald eine Aussage false"
ist, d.h. result->getData() wird nur ausgeführt, wenn result!=NULL"
(gilt immer bei logischen Ausdrücken, also immer in if ( ), while( ) usw. )"
"
"
❍ es muss der Inhalt (*d) und nicht der Zeiger (d) verglichen werden; "
ggf. muss der Operator != oder == geeignet überschrieben sein, z.B. bei Zeichenketten."
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.12 Algorithmen und Datenstrukturen 11.2.3 Einfügen von Listenelementen
Einfügen von newElem vor dem ersten Listenelement (,,prepend“)!
!
newElem ist im Folgenden immer ein Zeiger auf das neue Listenelement"
!
"1. Das erste Listenelement wird zum Nachfolger des neuen Listenelements"
"
"
newElem->setNext(first);
"
"2. first verweist auf das neue Listenelement newElem"
"
"
first = newElem;
// Zeiger auf neues Listenelement
first"
first"
2"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
newElem"
Objekt"
neu
Next"
1"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
Beachte: bei umgekehrter Reihenfolge ginge das Ziel des Zeigers „first“ verloren."
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.13 Algorithmen und Datenstrukturen 11.2.3 Einfügen von Listenelementen ...
Einfügen von newElem hinter einem gegebenen Listenelement (,,insert“)!
!
"1. Zunächst Navigation zum Listenelement curr, hinter dem eingefügt wird, z.B. mit find()
"
"2. next von newElem übernimmt den next-Zeiger des curr Listenelements"
" newElem->setNext(curr->getNext());
"
"3. next-Zeiger des aktuellen Listenelements verweist auf newElem
" curr->setNext(newElem);
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
first"
curr"
Objekt"
1
Objekt"
2
Next"
Next"
NULL"
newElem"
1"
3"
Objekt"
neu
Next"
2"
Objekt"
3
Objekt"
4
Next"
Next"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.14 Algorithmen und Datenstrukturen 11.2.3 Einfügen von Listenelementen ...
Einfügen von newElem als letztes Listenelement (,,append“)!
!
1. Navigation zum letzten Listenelement curr "
(Bedingung curr->getNext() == NULL)
"
2. Der next-Zeiger des aktuellen Listenelements wird auf newElem gesetzt"
curr->setNext(newElem);
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
first"
curr"
NULL"
1"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
2"
Next"
newElem"
Beachte: Bei leerer Liste ergibt sich curr ==first
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.15 Objekt"
neu
NULL"
und es wird einfach first=newElem gesetzt."
Algorithmen und Datenstrukturen 11.2.4 Löschen von Listenlementen
Löschen des ersten Listenelements!
!
1. Hole Zeiger auf zu löschendes Listenelement: delElem = first;
"
2. first-Zeiger auf nächstes Listenelement first = first->getNext();
"
3. Zeiger zurücksetzen & Löschen: delElem->next = NULL; delete delElem;
"
Beachte: Die Daten T, auf die delElem per Zeiger T* data verweist, werden nicht gelöscht! Bei Bedarf muss dies explizit hinzugefügt werden."
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
first"
Objekt"
1
2"
Next"
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
3"
delElem"
1"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
NULL"
11.16 Algorithmen und Datenstrukturen 11.2.4 Löschen von Listenlementen …
Löschen innerhalb der Liste!
!
"1. Navigiere zum Vorgänger curr des zu löschenden Elements und hole Zeiger"
" auf zu löschendes Element: delElem = curr->getNext();
"
"2. Passe next-Zeiger an: curr->setNext(delElem->getNext());
"
"3. Zeiger zurücksetzen & Löschen: delElem->next = NULL; delete delElem;
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
first"
curr"
1"
Objekt"
1
Objekt"
2
Next"
Next"
2"
Objekt"
3
Objekt"
4
Next"
Next"
1"
delElem"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
3"
NULL"
11.17 Algorithmen und Datenstrukturen 11.2.4 Löschen von Listenlementen …
Löschen des letzten Listenelements!
"
"
"1. Navigiere zum vorletzten Listenelement curr und hole Zeiger auf zu"
" löschendes Listenelement: delElem = curr->getNext();
" (Bedingung: curr->getNext()->getNext() == NULL)"
"2. Setze next-Zeiger auf NULL: curr->setNext(NULL);
"
"3. Löschen: delete delElem;
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
NULL"
first"
delElem"
curr"
1"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
2"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.18 Algorithmen und Datenstrukturen 11.2.5 Bewertung von verketteten Listen
Vorteile:!
!
"❍ Dynamische Länge"
"
"❍ Keine Speicherverschwendung durch Vorreservierung des Speichers"
"
"❍ Einfache Einfüge- und Löschoperationen ohne Kopieraufwand"
"
Nachteile:!
!
"❍ Zugriff immer sequentiell, Aufwand der Positionierung O(n)
"
"❍ Vorgänger von elem nur aufwändig erreichbar: Suche ab first bis"
" curr->getNext() == elem"
"
"❍ Operationen Einfügen und Löschen nur nach dem aktuellen Listenelement"
" einfach realisierbar"
"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.19 Algorithmen und Datenstrukturen 11.3 Doppelt verkettete Liste
Ziel: !Aufheben der Einschränkung der verketteten Liste hinsichtlich der Navigation"
!
"zum Vorgänger"
"
Beschreibung:!
!
"❍ Jedes Listenelement erhält zusätzlich zu dem next-Zeiger einen prev-Zeiger (previous) "
"
"
auf das vorhergehende Listenelement"
!
"❍ Damit ist die unmittelbare Navigation zum vorhergehenden Listenelement möglich"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.20 Algorithmen und Datenstrukturen 11.3.1 Aufbau einer doppelt verketteten Liste
Grundsätzlich ähnlicher Aufbau wie bei einfach verketteter Liste"
"
Listenelement für die doppelt verkettete Liste:!
!
class ListElem {
public:
ListElem(T∗ d) { // Konstruktor
next = NULL;
prev = NULL;
data = d;
}
T∗
getData(void)
{ return data; }
void
setNext(ListElem∗ n)
{ next = n; }
ListElem∗
getNext(void)
{ return next; }
void
setPrev(ListElem∗ n)
{ prev = n; }
ListElem∗
getPrev(void)
{ return prev; }
};
private:
T* data;
ListElem∗ next;
ListElem∗ prev;
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
Graphisches"
Symbol:
Daten/"
Objekt
Next"
prev"
// Zeiger auf Datenelement
// Zeiger auf das nächste Listenelement
// Zeiger auf das vorherige Listenelement
11.21 Algorithmen und Datenstrukturen 11.3.1 Aufbau einer doppelt verketteten Liste ...
first- und next-Zeiger wie bei einfach verketteter Liste:"
"
"❍ first verweist auf das erste Listelement der Sequenz"
"
"❍ next ist Teil des Listenelements und verweist auf den jeweiligen Nachfolger"
" bzw. ist NULL für das letzte Listenelement der Sequenz"
"
prev-Zeiger: ❍ verweist auf das vorhergehende Listenelement"
"
❍ bei dem ersten Element der Liste wird prev auf NULL gesetzt"
"
"
Beispiel:"
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.22 Algorithmen und Datenstrukturen 11.3.2 Einfügen in eine doppelt verkettete Listen ...
Einfügen von newElem vor dem ersten Listenelement (,,prepend“)!
!
"1. Das erste Listenelement wird zum Nachfolger des neuen Listenelements"
" newElem->setNext(first);
"
"2. Rückverknüpfung: first->setPrev(newElem);
"
"3. first zeigt auf das neue Element newElem: first = newElem;
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
first"
newElem"
3"
Objekt"
neu
Next"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
1"
prev"
2"
NULL"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.23 Algorithmen und Datenstrukturen 11.3.2 Einfügen in eine doppelt verkettete Listen ...
Einfügen von newElem hinter einem gegebenen Listenelement (,,insert“)!
!
"1. Zunächst Navigation zum Listenelement curr, hinter dem eingefügt wird"
"
"2. newElem übernimmt Zeiger auf seinen Vorgänger & Nachfolger:"
" newElem->setPrev(curr); newElem->setNext(curr->getNext());
"
"3. Zeiger auf newElem beim Vorgänger & Nachfolger setzen:"
" curr->setNext(newElem); newElem->getNext()->setPrev(newElem);
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
first"
curr"
newElem"
1"
Objekt"
1
Objekt"
2
Next"
Next"
Objekt"
neu
Next"
3"
prev"
prev"
2"
2"
prev"
3"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.24 Objekt"
3
Objekt"
4
Next"
Next"
prev"
prev"
NULL"
Algorithmen und Datenstrukturen 11.3.2 Einfügen in eine doppelt verkettete Listen ...
Einfügen von newElem als letztes Listenelement (,,append“)!
!
"1. Navigation zum letzten Listenelement curr"
"
"2. next-Zeiger des letzten Listenelements verweist auf newElem:"
" curr->setNext(newElem);
"
"3. prev-Zeiger von newElem verweist auf curr: newElem->setPrev(curr);
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
newElem"
first"
curr"
1"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
Objekt"
neu
Next"
2"
prev"
3"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.25 NULL"
Algorithmen und Datenstrukturen 11.3.3 Löschen von Elementen
Löschen des ersten Listenelements!
!
"1. Hole Zeiger auf zu löschendes Listenelement: delElem = first;
"
"2. first-Zeiger auf nächstes Listenelement first = first->getNext();
"
"3. Rücksetzen & Löschen: first->prev=NULL; delElem->next=NULL; delete delElem;
"
Beachte: Die Daten, auf die delElem verweist, werden nicht gelöscht!"
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
first"
2"
delElem"
1"
Objekt"
1
Next"
prev"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
3"
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
prev"
prev"
prev"
NULL"
NULL"
11.26 Algorithmen und Datenstrukturen 11.3.3 Löschen von Elementen …
Löschen innerhalb der Liste!
!
"1. Navigiere zum zu löschenden Listenelement (Zeiger): delElem"
"
"2. Passe next an: delElem->getPrev()->setNext(delElem->getNext());
"
"3. Passe prev an: delElem->getNext()->setPrev(delElem->getPrev());
"
"4. Löschen: delElem->next = NULL; delElem->prev = NULL; delete delElem;
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
first"
delElem"
2"
1"
Objekt"
1
Objekt"
2
Next"
Next"
prev"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
prev"
3"
4"
Objekt"
3
Objekt"
4
Next"
Next"
prev"
prev"
NULL"
NULL"
11.27 Algorithmen und Datenstrukturen 11.3.3 Löschen von Elementen …
Löschen des letzten Listenelements!
!
"1. Navigiere zum vorletzten Listenelement curr und hole Zeiger auf zu"
" löschenden Elements: delElem = curr->getNext();
"
"2. Setze next-Zeiger auf NULL: curr->setNext(NULL);
"
"3. Zeiger zurücksetzen & Löschen: delElem->prev = NULL; delete delElem;
first"
Objekt"
1
Objekt"
2
Objekt"
3
Objekt"
4
Next"
Next"
Next"
Next"
prev"
prev"
prev"
prev"
NULL"
NULL"
first"
curr"
delElem"
1"
Objekt"
1
Objekt"
2
Objekt"
3
Next"
Next"
Next"
prev"
prev"
prev"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
Objekt"
4
2"
Next"
3"
NULL"
11.28 1"
prev"
NULL"
Algorithmen und Datenstrukturen 11.3.4 Bewertung von doppelt verketteten Listen
Vorteil gegenüber einfach verkettete Liste:"
!
❍ Vorgängersuche: O(1)
"
Vor- und Nachteile sonst wie bei einfach verketteten Listen"
!
Bemerkung: Die Rückverknüpfung nutzt nur bei lokalen Operationen etwas,"
"
das globale Suchen eines Listenelements ist genauso aufwändig, da man"
"
nicht weiß, in welcher Richtung man suchen muss."
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.29 Algorithmen und Datenstrukturen 11.4 Datenstruktur Stack
Erinnerung!
!
Einsatz z.B. zur Speicherung automatischer Variablen (siehe Abschnitt 6.15)"
!
Stack-Konzept: Stapelspeicher nach dem Last-In-First-Out (LIFO) Prinzip:!
❍ push: neues Element auf den Stapel ablegen (Variablen-Deklaration)!
❍ pop: oberstes Stapel-Element (Kopf, Head) holen & vom Stapel entfernen (Blockende)"
❍ initial ist ein Stack immer leer"
❍ Hilfsfunktionen eines Stacks:"
"
❑ getHead( ): Holt oberstes Element ohne es zu entfernen"
❑ isEmpty( ): Prüft, ob Stack leer ist (Ergebnis: boolsch)"
"
"
Beispiel zum Stack-Konzept: Speicherung automatischer Variablen beim Ablauf eines Programms"
initial leer
push(int, i)
i
push(float, f)
f
i
push(float, ff)
ff
f
i
isEmpty() == true
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.30 Algorithmen und Datenstrukturen 11.4.1 Umsetzung eines Stacks
Umsetzung eines Stacks als Array!
!
Verwendung eines Arrays mit fester Länge"
"
"+ dies ist Laufzeit-effizienter als eine lineare Liste, da keine (De-)Allokationen verwendet werden"
"
"– übliche Nachteile, insbesondere muss die max. Anzahl der Elemente vorallokiert werden"
"
Umsetzung unter Vermeidung von Kopier-Operationen:!
!
"❍ erstes Array-Element ist das ,,älteste“ Element im Stack"
"
"❍ der Kopf bewegt sich, z.B. beim push nach rechts!
push(S,e2)
a[0] a[1] a[2] a[3]
a[0] a[1] a[2] a[3]
e0 e1
e0 e1 e2
head
head
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.31 Algorithmen und Datenstrukturen 11.4.1 Umsetzung eines Stacks …
Umsetzung eines Stack als Array!
Ist sehr gut geeignet, wenn zu erwartende Länge des Stacks einigermassen vorhersehbar ist."
Beachte: Im Unterschied zur verketteten Liste erfolgt hier das Einfügen und Löschen nur auf dem obersten
Element des Stapels (bei head) und bildet daher ein einfacheres Problem."
Head liegt am Ende der Liste im Array, wandert bei push/pop nach hinten."
!
Umsetzung eines Stack als lineare Liste (dynamische Speicherverwaltung)"
Ist erforderlich, wenn Länge zur Laufzeit dynamisch mitwachsen soll."
Wir greifen auf die einfach verkettete Liste und Datentyp ListElem zurück. "
Head liegt am Anfang der verketteten Liste und bleibt dort. Mit push werden Elemente vorne eingefügt."
"
head"
Objekt"
4
Objekt"
3
Objekt"
2
Objekt"
1
Next"
Next"
Next"
Next"
NULL"
head"
newElem"
push:
Objekt"
neu
Objekt"
4
Objekt"
3
Objekt"
2
Objekt"
1
Next"
Next"
Next"
Next"
Next"
NULL"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.32 Algorithmen und Datenstrukturen 11.4.1 Bemerkungen
Zur Implementierung mit verketteter Liste:!
!
"❍ würde man bei push( ) das neue Element hinten an die Liste anfügen und den Zeiger head auf
das neue Element weiterrücken, so gäbe es bei pop( ) keinen Zeiger mehr zurück auf das vorige
Element. Deshalb muss der Stack nach vorne (im Bild nach links) wachsen:"
head"
"
newElem"
"
"
Objekt"
Objekt"
Objekt"
Objekt"
Objekt"
neu
4
3
2
1
"
"
Next"
Next"
Next"
!
!
Zur Implementierung mit einem Array:"
Next"
Next"
NULL"
"
"❍ würde man bei push( ) das neue Element vorne im Array (index 0) eintragen, sodass der Zähler
head nicht erforderlich wäre und das im Stack oben liegende Element stets in a[0] zu finden wäre, so
müssten bei jeder push oder pop Operation alle Elemente des Stacks aufwändig verschoben (und dabei
kopiert) werden. Deshalb muss der Stack nach hinten (im Bild nach rechts) wachsen."
a[0] a[1] a[2] a[3]
e0 e1 e2
head
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.33 Algorithmen und Datenstrukturen 11.4.1 Umsetzung eines Stacks …
Umsetzung eines Stack als lineare Liste!
!
Head entspricht dem first-Zeiger der linearen Liste"
!
Datenstruktur aufbauend auf ListElem der einfach verketteten Liste, wobei"
der Typ int (statt T∗) verwaltet wird"
class Stack {
public:
Stack() { head = NULL; }
int pop(void) // Loeschen am Kopf
{
if ( isEmpty() ) {
cout << "Error: Pop empty stack\n";
return -1; // -1 heisst Fehler
}
int result = head->getData();
~Stack()
{ while( !isEmpty() ) pop(); }
bool isEmpty(void) { return head == NULL; }
int getHead(void) // Kopf holen
{
if ( isEmpty() ) {
cout << "Error: Empty stack\n";
return -1; // -1 heisst Fehler
}
else return head->getData();
}
void push(int d) // Einfuegen am Kopf
{
ListElem *newElement = new ListElem(d);
newElement->setNext(head);
head = newElement;
}
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
// Kopf merken und loeschen
ListElem *tmp = head;
head = head->getNext();
delete tmp; // alten Kopf loeschen!
}
return result;
private:
ListElem *head; // aktuellen Stack-Kopf
};
11.34 Algorithmen und Datenstrukturen 11.4.1 Umsetzung eines Stacks …
Bemerkungen:!
!
"❍ der Stack speichert nur den Verweis auf das erste Listenelement"
"
"❍ push() entspricht dem prepend (siehe Abschnitt 11.2.3)"
"
"❍ pop() und getHead() müssen auf einen leeren Stack prüfen"
"
"❍ pop() entspricht dem removeFirst() (siehe Abschnitt 11.2.4) und"
" muss dafür Sorge tragen, dass das Listen-Element gelöscht wird"
"
Anwendungsbeispiele neben der Variablenverwaltung"
"
"❍ allgemeine Formen der Rekursion"
"
"❍ Syntaxanalyse von Programmen, zur Zuweisung der passenden BNF Regel"
"
"❍ Auswertung logischer oder arithmetischer Ausdrücke"
"
"❍ Speicherung von Funktionsparametern und von lokalen Variablen in"
" der Ausführung von C++"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.35 Algorithmen und Datenstrukturen 11.4.2 Stack-Beispiel
Beispiel: Stapelrechner!
!
Ziel: Umsetzung eines Taschenrechners mit Postfix-Syntax"
"
Postfix-Syntax: ❍ Positionierung von Operatoren nach den Operanden"
"
"
❍ Operator verarbeitet Operanden links von sich"
"
❍ Operationsergebnis ersetzt Sequenz aus Operanden und Operator"
"
"
"
2 + 4 ∗ 5 entspricht:
245∗+
→
2(45∗)+
→
2 20 +
→
22"
"
Vorteile: ❍ keine Klammerung notwendig"
!
"❍ sehr einfache Implementierung eines Rechners"
"
Nachstehender Code geht davon aus, dass"
!
"❍ die Eingabe in Form einer Zeichenkette erfolgt (Beispiel: 2 4 5 ∗ +)"
""
"❍ die Zeichenkette besteht aus"
"
"
"❑ Operatoren ∗, + oder"
"
"❑ einstellige, vorzeichenlose Zahlen 0,...,9"
"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.36 Algorithmen und Datenstrukturen 11.4.2 Stack-Beispiel …
Code für den Stapelrechner auf Basis des int-Stacks"
"
"int evaluate(char ∗ str)
{
Stack s;
int i = 0;
while ( str[i] != ’\0’ ) {
if ( str[i] == ’+’ ) {
s.push(s.pop() + s.pop());
// beginne am Anfang der Zeichenkette
//
//
//
//
String-Ende noch nicht erreicht?
Addition
hole beide Operanden und
pushe Ergebnis auf Stack
}
else if ( str[i] == ’∗’ ) {
//
s.push(s.pop() ∗ s.pop()); // hole
//
}
else if ( str[i] >= ’0’ && str[i] <=
s.push(str[i]-’0’);
//
}
i++;
//
}
}
return s.pop();
Multiplikation
beide Operanden und
pushe Ergebnis auf Stack
’9’ ) {
Ziffer = Differenz von ASCII – Werten.
nächstes Zeichen
"❍ alle anderen Zeichen, z.B. Leerzeichen werden hier ignoriert"
"
"❍ str[i]-’0’ wandelt das ASCII-Zeichen str[i] in zugehörigen int um.
"❍ Bei Operatoren ‚-‘ und ‚/‘ (Subtraktion und Division) müsste man mit der Reihenfolge der beiden pop-Operationen vorsichtig sein (Kommutativgesetz!). "
"
Prof. Dr. Volker Blanz
"
Fachgruppe Medieninformatik"
11.37 Algorithmen und Datenstrukturen 11.4.2 Stack-Beispiel …
Beispiel: Auswertung mit dem Stapelrechner!
!
Eingabe: 2 4 5 ∗ + 7 ∗ (entspricht ((4 ∗ 5) + 2) ∗ 7)"
"
Abarbeitung im Stapelrechner"
Zeichen
!
2
Operation:
p
4
2
4
2
p
∗
5
5
4
2
p
+
20
2
ppp
∗
7
22
ppp
7
22
p
154
ppp
(p = push, ppp=push(pop OP pop)
Ergebnis: 154"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.38 Algorithmen und Datenstrukturen 11.4.2 Stack-Beispiel …
Bewertung von Stacks!
!
Fehlerfälle bei der Nutzung eines Stacks:"
!
"❍ Stack Underflow: Lesen eines Elements aus einem leeren Stack"
!
"❍ Stack Overflow: Ist der Stack mit einem Array umgesetzt, führt ein"
!
"
push auf einen vollen Stack zu einem Overflow-Fehler"
"
Vorteile: ❍ sehr nützlich zur Umsetzung von Rekursionen (implizite Nutzung"
!
"
des Stacks im Rechner)"
"
"❍ dynamische Anzahl von Elementen (bei Umsetzung mit linearer Liste)"
"
"❍ sehr effizient, Zugriff, Einfügen und Löschen ist immer O(1)
"
Nachteile: ❍ kein Zugriff auf andere Elemente als das Kopf-Element"
!
"
❍ kann nur ein LIFO-Konzept umsetzen"
"
"
Hier kommt es darauf an, den Stack nur in den geeigneten Situationen einzusetzen!"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.39 Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue
Queue (Aussprache [kju:], engl. Warteschlange) setzt das FIFO-Prinzip (first-in-first-out) um"
!
Bedeutung: Schlange oder Warteschlange"
!
Queue-Konzept:!
!
"❍ Einfügen von Elementen am Ende (tail)"
!
"❍ Entfernen von Elementen am Anfang (head)"
!
Methoden einer Queue:"
!
"❍ enqueue(x): Einfügen von x am Ende der Warteschlange"
"
"❍ dequeue(): Holen und Entfernen des ersten Elements (Head)"
!
"❍ getHead(): Holen des ersten Elements ohne Entfernen!
!
"❍ isEmpty(): Prüft, ob Queue leer ist (Ergebnis: boolsch)"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.40 Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue …
Umsetzung einer Queue!
!
Grundsätzlich sind zwei Zeiger zu speichern:"
"
"❍ head verweist auf das erste (älteste) Element"
"
"❍ tail verweist auf das letzte (neueste) Element oder, je nach Umsetzung,"
"
"
auf den ersten freien Platz für das nächste Einfügen"
"
Umsetzungsvarianten für eine Queue:"
"
"1. Dynamische Speicherzuweisung:
Doppelt verkettete Liste.!
"2. Statische Speicherzuweisung:
Array in Form eines Ringpuffers!
"
!
Doppelt verkettete Liste mit eingeschränkter Funktionalität"
"
"❍ Einfügen nur am Ende (enqueue())"
"
"❍ Löschen nur am Anfang (dequeue())"
"
"❍ zusätzlicher Zeiger last zum schnellen Zugriff auf letztes Listenelement bei dequeue"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.41 Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue …
Warum ein Ringpuffer? !
Anders als beim Stack wird immer am tail hinzugefügt und am head gelöscht."
Dadurch wandert der belegte Speicherbereich über das Array."
"
head
tail
Ohne Ringpuffer: Stand… "
nach 6 enqueue()-Operationen:"
"
nach einem weiteren enqueue"
"
nach einem dequeue"
"
nach einem weiteren enqueue"
"
nach einem dequeue"
"
head
tail
"
Ring
"
Selbst wenn die Länge der Schlange ungefähr konstant bleibt, wird sie nach längerem Gebrauch aus dem
Speicherbereich des Arrays hinauslaufen. (Beachte: bei menschlicher Warteschlange gehen alle nach
vorne, hier dagegen soll ein Aufrücken vermieden werden. Z.B. Wartezimmer mit Stühlen…) "
"
Ringpuffer: Verknüpfe letztes Element des Arrays zyklisch mit dem ersten. Dimensioniere den Ring etwas
größer als zu erwartende Maximallänge der Schlange (sonst kollidiert tail mit head). Bei dauerhaftem
Gebrauch wandert sie zyklisch immer wieder durch den Ring. "
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.42 Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue …
Umsetzung als Ringspeicher!
!
Datenstruktur: Nutzung eines (linearen) Arrays, das durch Modulo-Operation zyklisch genutzt wird"
a[0]
a[1]
a[2]
a[3]
a[4]
a[5]
a[6]
a[7]
"relevanter Index für n aus ℕ : n%8 (Modulo)"
Zeiger für head und tail werden als Indizes"
"umgesetzt; tail verweist dabei auf"
"den nächsten freien Platz!
!
Modulo % arbeitet nur mit unsigned int korrekt."
⇒ head und tail müssen anwachsen"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.43 tail
head
Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue …
Fehlerfälle!
!
Overflow: Enqueue auf eine volle Queue (enqueue liefert false als Ergebnis)"
Bei Ringspeicher: tail kollidiert mit head."
"
Underflow: Dequeue auf eine leere Queue (dequeue liefert false als Ergebnis)"
"
Feststellen des Zustands eines leeren/vollen Stacks mit n Elementen:"
"
"❍ head == tail zeigt an, dass entweder"
❑ kein Element gelesen werden kann, falls head ,,unbesetzt“"
❑ kein Element geschrieben werden kann, falls head ,,besetzt“"
(Beachte: hier ist tail als erster freier Platz vor der queue definiert)."
"
⇒verwende Array der Länge n + 1, um Queue der Maximallänge n zu speichern, und die Vereinbarung"
❑,,Queue leer“: head == tail"
❑,,Queue voll“: head == (tail+1)%(n+1) d.h. es bleibt noch ein Element des Ringspeichers frei"
"
Bewertung Queue auf Array-Basis!
!
Vorteile: ❍ Umsetzung von Warteschlangen (z.B. für GUI-Ereignisverarbeitung)"
"
❍ Einfügen und Löschen immer O(1)
"
Einschränkung: Setzt nur das FIFO-Prinzip um"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.44 Algorithmen und Datenstrukturen 11.5 Datenstruktur Queue …
Umsetzung einer Queue mit Array!
// Queue-Klasse auf Array-Basis
class Queue
{
public:
Queue(unsigned int maxNumber) {
length = maxNumber+1;
arr = new T*[length]; // Allokation.
head = 0;
tail = 0;
}
~Queue() { delete [] arr; }
bool isEmpty(void) // ist Queue leer ?
{ return head == tail; }
bool isFull(void) // ist Queue voll ?
{ return head == (tail+1)%length; }
T* getHead(void) // holen des Kopfs
{
if ( isEmpty() ) {
cout << "queue is empty!\n";
return NULL;
}
else return arr[head];
}
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
bool enqueue(T* d) // Einfuegen bei tail
{
if ( isFull() ) { // ist Queue voll?
cout << "queue is full!\n";
return false;
}
arr[tail] = d; // einfuegen
tail = (tail+1) % length;
return true; // Einfuegen erfolgreich!
}
bool dequeue(T* *data) // Entfernen bei head
{
if ( isEmpty() ) { // queue leer?
cout << "queue is empty!\n";
return false;
}
*data = arr[head]; // Rueckgabedaten
head = (head+1) % length;
return true;
}
private:
T* *arr; // array mit T-Zeigern
unsigned int length; // Laenge des array
unsigned int head; // head-Index
unsigned int tail; // tail-Index
};
11.45 Algorithmen und Datenstrukturen 11.6 Anwendung von Queues
Grundsätzlich: Umsetzung eines Erzeuger-Verbraucher- (producer-consumer)"
"
"bzw. eines Pipeline-Prinzips"
"
"❍ Erzeuger produziert eine Folge von Daten"
"
"❍ Verbraucher nimmt Daten entgegen und verarbeitet sie weiter"
"
"❍ Verallgemeinert: Folge von Verarbeitungsprozessen (Pipeline)"
"
Queues stellen die Verbindung zwischen den einzelnen Entitäten her"
head
Verarbeitungs−"
"
prozess 1
tail
dequeue( )
head
tail
Verarbeitungs−"
"
prozess 2
enqueue( )
dequeue( )
Verarbeitungs−"
"
prozess 3
enqueue( )
Queue A
Queue B
Beachte: Dieses Konzept ist vor allem dann sinnvoll, wenn"
"
"❍ Verarbeitungsprozesse mit einem Teil der Ergebnisdaten des"
"
"
Vorgängers bereits arbeiten können und"
"
"❍ die Verarbeitungsprozesse asynchron (unabhängig) sind"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.46 Algorithmen und Datenstrukturen 11.6 Anwendung von Queues …
Beispiel: Polyas Sieb!
!
Polyas Sieb: Berechnung der Kubikzahlen 13, 23, 33, . . . Mittels"
"Zahlen 1, ..., 20:
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, . . . , 20"
"Lösche jede 3. Zahl: 1, 2, 4, 5, 7, 8, 10, 11, 13, 14, 16, 17, 19, 20"
"Bilde Teilsummen:
1, 3, 7, 12, 19, 27, 37, 48, 61, 75, 91, 108, 127, 147"
"Lösche jede 2. Zahl: 1, 7, 19, 37, 61, 91, 127"
"Bilde Teilsummen:
1, 8, 27, 64, 125, 216, 343"
"
Umsetzung mittels Queues!
"❍ Queue-Klasse speichert teilweise Zwischenergebnisse"
"❍ Verarbeitungschritte holen ein Element aus Eingangs-Queue, verarbeiten"
" dieses und schreiben das Ergebnis in die Ausgangsqueue"
"
Unser Problem: Echte asynochrone Verarbeitung der einzelnen Schritte in"
"einem einfachen Programm nicht möglich."
"Lösung: Wir rufen die 5 Schritte in Polyas Sieb in zufälliger Reihenfolge auf."
"
"
"
Bemerkung: Betrachte Quadratzahlen 1, 4, 9, 16, 25, …"
Differenz dazwischen:
3 5 7
9"
"
Begründung: (n + 1)2 = n2 + 2n + 1, 2n + 1 sind die ungeraden Zahlen."
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.47 Algorithmen und Datenstrukturen 11.6 Anwendung von Queues …
Zur Umsetzung!
!
Verarbeitungsstufen: Werden als Programme umgesetzt, konkret"
"
"❍ remove3(Queue& in, Queue& out) liest Element von in"
" und schreibt es in out, falls es nicht ein drittes Element ist (gemäß internem Zähler)."
" (remove2(Queue& in, Queue& out) wird analog dazu definiert - entfernt jedes zweite Element.)"
"
"❍ partialSum(Queue& in, Queue& out) liest Element von in und"
" schreibt die Summe aus diesem mit der letzten Partialsumme in out"
// Jede 3. Zahl entfernen
void remove3(Queue& in, Queue& out)
{
int curr;
// merke: Zahl der gelesenen Elemente
static int numRead = 0;
// Erste Teilsumme
void partialSum1(Queue& in, Queue& out)
{
int curr;
// merke: Bisherige Teilsumme
static int res = 0;
// neue Zahl in ’in’ & Platz in ’out’
if (!in.isEmpty() && !out.isFull()) {
in.dequeue(&curr);
numRead ++;
if ( numRead%3 != 0 )
out.enqueue(curr);
}
return;
}
// neue Zahl in ’in’ und Platz in ’out’
if ( !in.isEmpty() && !out.isFull()) {
in.dequeue(&curr);
res += curr;
out.enqueue(res);
}
return;
}
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.48 Algorithmen und Datenstrukturen 11.6 Anwendung von Queues …
Implementierung von Polyas Sieb"
// Jede 3. Zahl entfernen
void remove3(Queue& in, Queue& out)
{ ... } // siehe oben
case 0: // Schritt 1
generateNumbers(q1);
break;
case 1: // Schritt 2
remove3(q1, q2);
break;
case 2: // Schritt 3
partialSum1(q2, q3);
break;
case 3: // Schritt 4
remove2(q3, q4);
break;
case 4: // Schritt 5
partialSum2(q4, q5);
break;
}
// Jede 2. Zahl entfernen
void remove2(Queue& in, Queue& out)
{ ... } // analog dazu
// Erste Teilsummen
void partialSum1(Queue& in, Queue& out)
{ ... } // siehe oben
// Zweite Teilsummen
void partialSum2(Queue& in, Queue& out)
{ ... } // analog dazu
int main()
{
float r;
Queue q1(20);
Queue q2(20);
Queue q3(20);
Queue q4(20);
Queue q5(20);
//
//
//
//
//
while ( !q5.isFull() ) {
r = 5.0 * rand()/(RAND_MAX+1); // Zufallszahl
switch((int) r ){
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
}
int result;
while ( !q5.isEmpty() ) {
q5.dequeue(&result);
cout << result << ", ";
}
cout << endl;
return 0;
Zahlen 1...n
ohne jede 3. Zahl
Teilsummen
ohne jede 2. Zahl
Teilsummen
11.49 }
Algorithmen und Datenstrukturen 11.6 Anwendung von Queues …
Bemerkung: Queues & Beispiel mit Polyas Sieb!
!
Die Funktion rand() ist in stdlib.h deklariert und liefert Zufallszahlen aus {0,...,RAND_MAX},"
wobei RAND_MAX eine vordefinierte (große) Konstante ist."
"
Hilfsfunktionen generateNumbers(), remove() und partialSum() sind auf"
!
die Verarbeitung von Teilergebnissen des vorherigen Schrittes ausgelegt"
"
und speichern den aktuellen Stand mittels static-Variablen"
"
Der logische Aufbau der Verarbeitungspipeline sieht so aus"
generateNumbers( )
Erzeugung"
"
der Zahlen
head
tail
dequeue( )
remove3( )
Entfernen "
"
jeder 3. Zahl
enqueue( )
dequeue( )
partialSum1( )
Bilde"
"
Teilsummen
enqueue( ) …
enqueue( )
Queue q1
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
head
tail
Queue q2
11.50 Algorithmen und Datenstrukturen 11.7 Sequentielle Datenstrukturen der STL
Container-Klassen: Allgemeine Klassen zur Speicherung beliebiger Datenelemente"
"
"Beispiele: Liste, Stack, Array"
"
Beispielklassen der Standard-Template-Library (STL)"
!
"❍ vector: Wichtigste Container-Klasse – realisiert ein ,,Array mit dynamischer Größe“"
"
"❍ list: (Doppelt) verkettete Liste"
"
"❍ queue"
"
"❍ priority-queue (sortierte Queue). "
Größtes Element steht am Anfang, push sortiert Elemente gemäß ihrer Größe in die Queue ein. "
"
"❍ stack: Stack-Implementierung"
"
"❍ map: Enthält Datenpaare, zum Beispiel für eine Lookup-Table zum Nachschlagen von Werten."
"
Vorteile der STL-Container-Klassen:"
!
"❍ Einfachheit und Effizienz in der Anwendung"
"
"❍ Einheitliche bzw. ähnliche Schnittstellen"
"
"❍ Typsicherheit (d.h. Vermeidung von Programmierfehlern, die entstehen, wenn versehentlich Zuweisungen zwischen unterschiedlichen Datentypen auftreten)"
und Homogenität (Containerklasse für alle zu speichernde Datentypen gleich)"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.51 Algorithmen und Datenstrukturen 11.7 Sequentielle Datenstrukturen der STL ...
Voraussetzung für die Verwendung der Container-Klassen:"
"
"Einbinden der entsprechenden (gleichnamigen) Header-Dateien, z.B."
"
"#include <vector>"
"
"
Template-Konzept: Alle STL-Containerklassen sind als Template-Klassen umgesetzt"
"
"❍ Template-Klassen haben zusätzliche Parameter, die zur Objekt-"
"
"
Instantiierung angegeben werden müssen"
"
"❍ Bei STL-Containerklasse: Angabe des Typs der zu verwaltenden Daten"
"
"
list<int> v;
"
"// Liste von int-Zahlen
"
"❍ Vorteil: Nur eine Klasse für alle denkbaren Datentypen"
"
"
Datenelemente besitzen einen festen Typ (wie bisher auch)"
"
"❍ Angabe des Typen mit Hilfe des Template-Parameters"
"
"❍ Template-Mechanismus bietet Typsicherheit, "
d.h. nur Datenelemente des gegebenen Typs können verwaltet werden. Dadurch werden Programmierfehler vermieden."
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.52 Algorithmen und Datenstrukturen 11.7.1 Die vector-Klasse
vector-Klasse: Kombinierte Array- und Listenfunktionalität mit dynamisch vielen Datenelementen."
"
Beispiel einer Array- ähnlichen Nutzung!
!
"vector<string>
phoneBook[0] =
phoneBook[1] =
phoneBook[2] =
phoneBook(1000);
"Stroustrup";
"Cormen";
"Hofstadter";
// Hinweis: ca. 1000 sind nötig
"
Bemerkung: Bei Verwendung des [ ]-Operators wird (wie bei einem Array)"
!
"der Index nicht automatisch auf Gültigkeit geprüft!"
"
"phoneBook[1000] = "Woo";
// Überschreitung des Grenze 0 bis 999.
"
"führt zu einem undefinierten, fehlerhaften Verhalten:"
"
Aktuelle Größe kann mit size() erfragt werden:"
!
"cout << "Anzahl Elemente: " << phoneBook.size();"
"
Anpassung der Größe mit resize(. . . ):"
!
"phoneBook.resize(2000);"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.53 Algorithmen und Datenstrukturen 11.7.1 Die vector-Klasse …
Beispiel einer Listen- ähnlichen Verwendung"
!
"vector<string> phoneBook;
// leere Liste
phoneBook.push_back("Stroustrup");
phoneBook.push_back("Cormen");
phoneBook.push_back("Hofstadter");
// Anfügen eines Elements
"
Navigation und Positionierung in STL-Containerklassen durch Iteratoren!
!
"❍ Ermöglicht das Durchlaufen sequentieller STL-Datenstrukturen"
"
"❍ Abstraktion vom Zeigerbegriff:"
"
"
❑ eigene Klasse zu jedem STL-Containertyp"
❑ Verwendung wie ein Zeiger unmittelbar auf die Datenelemente!
!
"❍ Iterator-Klassen entsprechen ziemlich genau Zeigern auf unsere Klasse ListElem, haben"
" aber weitere Funktionalität, z.B."
"
"
"
❑ Operator ++ geht auf das nächste Listenelement"
❑ Operator [ ] (Parameter int) geht zum i-ten Listenelement"
❑ Dereferenzierungs-Operator ∗ greift auf Datenelement zu"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.54 Algorithmen und Datenstrukturen 11.7.1 Die vector-Klasse …
Bemerkung: "
Intern ist die vector-Klasse als array implementiert. Die Elemente der dynamischen Speicherverwaltung
(resize, push back) sind durch Umkopieren und durch eine Speicherallokierung mit einem gewissen Vorrat
an Elementen umgesetzt.
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.55 Algorithmen und Datenstrukturen 11.7.1 Die vector-Klasse …
Beispiel zur Navigation in der vector-Klasse"
!
"vector<string>::iterator i;
for (i = phoneBook.begin();
i != phoneBook.end();
i++)
// Instanziierung des Iterators
// erstes Element
// solange nicht am Ende
// hole nächstes Element
cout << "Eintrag: " << ∗i << "\n";
// dereferenziere Iterator
"
!
Hinzufügen von Elementen mit insert"
!
"phoneBook.insert(phoneBook.begin(), "Woo"); // Einfuegen vor dem
// ersten Element!
!
Entfernen von Elementen mit erase!
!
"phoneBook.erase(phoneBook.begin());
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.56 // Erstes Element löschen
Algorithmen und Datenstrukturen 11.7.1 Die vector-Klasse …
Verwendung eines Iterators: Hochzählen bewirkt ein Aufsteigen in der Liste
i = phone_book.begin( )
i == phone_book.end( )
i+1
Stroustrup"
"
+1
-1
(Stroustrup, Cormen, Hofstadter)."
Cormen"
"
i+2
+1
-1
Hofstadter"
"
+1
"
Verwendung eines reverse_iterator: Hochzählen bewirkt ein Absteigen in der Liste
i = phone_book.rbegin( )
i == phone_book.rend( )
i+1
Hofstadter"
"
+1
-1
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
(Hofst., Cormen, Stroustrup)."
Cormen"
"
i+2
+1
-1
11.57 Stroustrup"
"
+1
"
Algorithmen und Datenstrukturen 11.8 Zusammenfassung
Wichtige Erkenntnisse und Inhalte dieses Abschnitts:"
"
Arrays sind zur Verwaltung von Objektmengen ungeeignet, falls die Objektzahl"
"stark variiert oder die Reihenfolge häufig geändert werden muss"
"
Sequentielle Datenstrukturen bilden eine Alternative, wobei der Zugriff auf"
"einzelnen Elemente im Allgemeinen aufwändiger oder eingeschränkt ist"
"
Standard-Operationen auf sequentielle Datenstrukturen"
!
"❍ Einfügen und Löschen an beliebiger oder eingeschränkter Position"
"
"❍ Zugriff auf Elemente in der Sequenz"
"
Typen von sequentiellen Datenstrukturen:!
!
"❍ Einfach verkettete Listen: Beliebiges Einfügen und Löschen, iterative"
" Navigation (nur vorw¨arts)"
"
"❍ Doppelt verkettete Listen: Beliebiges Einfügen und Löschen, iterative"
" Navigation (vorwärts und rückwärts)"
"
"❍ Stack: LIFO-Prinzip, Einfügen und Löschen an head-Position"
"
"❍ Queue: FIFO-Prinzip, Einfügen bei tail, Löschen bei head"
Prof. Dr. Volker Blanz
Fachgruppe Medieninformatik"
11.58 Algorithmen und Datenstrukturen 
Herunterladen