Datenstrukturen & Algorithmen Matthias Zwicker Universität Bern Frühling 2010
Übersicht Elementare Datenstrukturen für dynamische Mengen Stapel & Warteschlangen Verkettete Listen Bäume Anwendungsbeispiel: kd-bäume 2
Dynamische Mengen Daten speichern und abfragen Daten: Records aus Schlüssel und Satellitendaten class Record { class SatelliteData{ int key; String name; SatelliteData data; String address;... 3
Operationen Dynamische Menge S, Schlüssel k, Referenz auf Record x Search(S,k) Insert(S,x) Delete(S,x) Minimum(S) Maximum(S) Successor(S,x) Predecessor(S,x) 4
Datenstrukturen Geeignete Datenstruktur je nach Operationen, die effizient unterstützt werden soll Zeitkomplexität wird als Funktion der Grösse n der Menge gemessen Beispiel: Heaps Abfragen von Minimum, Maximum in O(1) 5
Stapel & Warteschlangen Dynamische Mengen mit effizientem Einfügen und Entfernen in O(1) Insert(S,x) x = Remove(S) Stapel Einfügen und Entfernen mit last-in-, first- out-strategie (LIFO) Warteschlangen Einfügen und Entfernen mit first-in-, first- out-strategie (FIFO) 6
Stapel (LIFO) Einfügen = push, Entfernen = pop, Aufwand O(1) Hier: Implementation mit Feld Implementation mit anderen Datenstrukturen möglich Elemente hier Integers der Einfachkeit halber, Records mit Satellitendaten funktioniern gleich Statusvariable top Stack S 12345678 161410 8 7 top = 5 class Stack { private int top; private int S[]; /* constructor, methods push, pop */ 7
Stapel Beispiel push(4); push(6); pop; push(8); pop; pop; Stack S 12345678 161410 8 7 top = 5 8
Anwendung Verwaltung von Methodenaufrufen Speichert Rücksprungadresse und lokale Variablen Aufruf einer Methode Push: Rücksprungadresse Push: Speicherplatz für lokale Variablen der aufgerufenen Methode Ende der Methode Pop: lokale Variablen Pop: Rücksprungadresse 9
Anwendung Stack H; void H { void e { void l() { ; e(); ; ; l(); ; W(); ; o(); ; ; 10
Stack overflow Zu tiefe Verschachtelung von Aufrufen Zu hohe Rekursionstiefe, so dass Stack Speicher zu klein Führt zu Terminierung des Programms Beispiel: worst case Ablauf von Quicksort Rekursive Aufrufe können durch eigene Verwaltung eines Stacks vermieden werden Vorteile Kleinerer Aufwand Programm kann mehr Speicher für Stack allokieren wenn nötig 11
Anwendung Nicht-rekursiver QuickSort mit Stack void quicksort(int array[], int left, int right) { Stack leftstack = new Stack(); Stack rightstack = new Stack(); leftstack.push(left); rightstack.push(right); while(!leftstack.isempty()) { left = leftstack.pop(); right = rightstack.pop(); if(left<right) { int p = partition(array, left, right); leftstack.push(left); rightstack.push(p-1); leftstack.push(p+1); rightstack.push(right); 12
Warteschlange (FIFO) Einfügen = queue, Entfernen = dequeue, Aufwand O(1) Hier: Implementation mit Feld Implementation mit anderen Datenstrukturen möglich (z.b. verkettete Listen) Statusvariable kopf, ende ende zeigt auf erstes freies Element Queue Q 12345678 10 8 7 1614 kopf = 3 ende = 8 class Queue { private int kopf, ende; private int Q[]; /* constructor, methods queue, dequeue */ 13
Warteschlange Beispiel queue(6); queue(9); queue(11); dequeue; dequeue 12345678 Queue Q 10 8 7 1614 kopf = 3 ende = 8 14
Anwendungen Verwaltung von Aufträgen, die mittels einer first-come, first-serve Strategie bearbeitet werden sollen Z.B. Printer Server, Datenbankabfragen über Internet 15
Übersicht Elementare Datenstrukturen Stapel & Warteschlangen Verkettete Listen Bäume Anwendungsbeispiel: kd-bäume 16
Verkettete Listen Vorteil gegenüber Feldern: maximale Anzahl Elemente ist dynamisch Nachteil: Zugriff auf ein Element in konstanter Zeit mittels Index nicht möglich Können Stacks und Warteschlangen auch mit verketteten Listen implementieren Varianten Einfach verkettete Liste Doppelt verkettete Liste Zyklische Liste (Sortierte Liste) 17
Verkettete Listen Listenelemente bestehen aus Schlüssel (oder Referenz auf andere Daten) Zeiger next auf nachfolgendes Listenelement Doppelt verkettet: Zeiger prev auf vorheriges Listenelement Anfang: Zeiger head auf erstes Listenelement Ende: null Zeiger class LinkedList { private class ListElement { ListElement prev, next; int key; ListElement head; /* methods to add elements, etc. */ 18
Durchsuchen ListElement Search(int k) { ListElement cur = head; while(cur!=null ll && k!=cur.key) k cur = cur.next; return cur; 19
Einfügen Doppelt verkettete Liste void insert(listelement e) { e.next = head; if(head!=null) head.prev = e; head = e; e.prev = null; 20
Entfernen Doppelt verkettete Liste void iddlt(litel delete(listelement td){ if(d.prev!=null) d.prev.next = d.next; else head = d.next; if(d.next!=null) d.next.prev = d.prev; 21
Wächter Spezielles Wächterobjekt nil, um Randbedingungen zu vereinfachen Verknüpfte Liste zu einem Ring (zyklische Liste) Zeiger auf Wächter nil, ersetzt head Hier doppelt verkettete Liste class CyclicList { private class ListElement { ListElement prev, next; int key; ListElement nil; /* methods to add elements, etc. */ 22
Einfügen in Liste mit Wächter void insertelement(listelement e) { e.next = nil.next; nil.next.prev = e; nil.next = e; e.prev = nil; 23
Parameterisierte Klasse in Java class LinkedList<T> { private class Element { private Element prev, next; private T data; // Satellitendaten private Element nil; LinkedList() { nil = new Element(); nil.next = nil; nil.prev = nil; void insert(t e) {... /* add other operations */ 24
Zeiger und Objekte mit Feldern Was, wenn eine Programmiersprache keine Zeiger zur Verfügung stellt? Implementiere Zeiger und Objekte mit Feldern 25
Zeiger und Objekte mit Feldern Liste head 9 16 4 1 Mehrfelddarstellung next key prev 1 2 3 4 5 6 7 8 Einfelddarstellung 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 26
Allokieren und Freigeben von Objekten Wollen Operationen auf dynamischen Listen anbieten wie vorher Insbesondere, allokieren und einfügen neuer Objekte Allokieren ähnlich new() in Java Brauchen Zugriff auf freie Elemente in Felder Unterhalte zwei Listen Liste mit Daten Doppelt verknüpft Kopf heisst head Liste mit freien Objekten Einfach verknüpft Kopf heisst free next key prev 27
Allokieren und Freigeben x = allocateobject(); insert(x); key[x] = 25; delete(5); 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8 next key prev next key prev 1 2 3 4 5 6 7 8 next key prev 28
Übersicht Elementare Datenstrukturen Stapel & Warteschlangen Verkettete Listen Bäume Anwendungsbeispiel: kd-bäume 29
Gerichtete Bäume Verallgemeinerung von linearen Listen mit Verkettung zu einer Baumstruktur Wie bei Listen: Elemente enthalten Schlüssel (oder Referenzen auf Satellitendaten) Referenzen zur Verkettung Grundlage für viele Algorithmen z.b. Suchen Heute Binäre Bäume Bäume mit unbeschränktem Grad 30
Binäre Bäume class BinaryNode { BinaryNode parent, left, right; itk int key; 31
Bäume mit unbeschränktem Grad class Node { Node parent, firstchild, nextsibling; int key; 32
Übersicht Elementare Datenstrukturen Stapel & Warteschlangen Verkettete Listen Bäume Anwendungsbeispiel: kd-bäume 33
kd-bäume Problem Speichere Position einer Menge von Punkten im d-dimensionalen Raum Wollen effiziente Abfragen wie Gegeben Abfragepunkt, finde nächsten Nachbarn in der Punktmenge Finde k-nächste Nachbarn Finde alle Nachbarn in einem gewissen Radius Naiver Ansatz erlaubt Abfragen in O(n) ( ) 34
Kd-Bäume Räumliche Datenstruktur Aufbau der Datenstruktur hat mit räumlicher Anordnung der Daten zu tun Erlaubt effiziente Abfragen in O(lg n) Anwendungen Künstliche Intelligenz, Klassifikation http://en.wikipedia.org/wiki/k-nearest_neighbor_algorithm http://en.wikipedia.org/wiki/nearest_neighbor_search Computergrafik, Ray Tracing 35
Konstruktion kd-baum teilt Raum rekursiv entlang einer Split- Ebene in zwei Halbräume Jeder Knoten Speichert einen Punkt Definiert eine Split-Ebene durch den Punkt Split-Ebenen Sind immer achsenparallel Orientierung der Split-Ebene gegeben durch Tiefe im Baum Alle Punkte im linken Teilbaum liegen unter der Split-Ebene, im rechten Teilbaum über der Split- Ebene Siehe auch http://en.wikipedia.org/wiki/kd-tree p 36
Beispiel Menge von Punkten in 2D kd-baum 37
Konstruktion Balancierter kd-baum axis it ist eine Koordinatenachse (x oder y in 2D) cycle(axis) rotiert durch die Koordinaten- achsen: cycle(x) = y, cycle(y) = x node constructkdtree tkdt (set of points, axis) if set of points is empty return null; else { select median point along axis split set of points into set below and above median create node with location of median point node.left = kdtree(points below median, cycle(axis)) node.right = kdtree(points above median, cycle(axis)) return node 38
Einfügen Eingabe: neuer Punkt Ausgabe: kd-baum der neuen Punkt enthält Ablauf Traversiere kd-baum bis zu einem Blatt Bei jedem Knoten, entscheide basierend auf Vergleich von Position von Knoten und neuem Punkt, in welchen Teilbaum zu gehen Wenn Blatt erreicht, erstelle neuen Knoten, der dem Blatt als Kind angehängt wird 39
Einfügen Neuer Punkt (3,5) 40
Nächster Nachbar Input: Abfragepunkt, Punktemenge in kd-baum Output: Punkt im kd-baum, der am nächsten beim Abfragepunkt liegt Ablauf Traversiere kd Baum bis zu Blatt, in das der Abfragepunkt fällt Blattknoten ist Kandidat dat für nächster Nachbar Traversiere Baum zurück zur Wurzel, bei jedem Knoten Falls Knoten näher als bisheriger Kandidat, wird neuer Kandidat Falls im anderen Teilbaum näherer Punkt möglich, traversiere anderen Teilbaum 41
Beispiel Nächster Nachbar von Abfragepunkt (3.5, 4.5) kd-baum 42
Beispiel Nächster Nachbar von Abfragepunkt (6.5, 0) kd-baum 43
Nächstes Mal Kapitel 11: Hashtabellen 44