Datenstrukturen & Algorithmen Matthias Zwicker Universität Bern Frühling 2010 Übersicht Elementare Datenstrukturen für dynamische Mengen • Stapel p & Warteschlangen g • Verkettete Listen • Bäume • Anwendungsbeispiel: kd-Bäume 2 Dynamische Mengen • Daten speichern und abfragen • Daten: Records aus Schlüssel und Satellitendaten class Record { int key; SatelliteData data; } class SatelliteData{ String name; String address; ... } 3 Operationen • Dynamische Menge S, Schlüssel k, Referenz auf Record x – – – – – – – Search(S,k) Insert(S,x) Delete(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 Operationen, werden soll • Zeitkomplexität wird als Funktion der Grösse n der Menge gemessen • Beispiel: p Heaps p – 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-, firstout Strategie (LIFO) out-Strategie Warteschlangen g • Einfügen und Entfernen mit first-in-, firstout Strategie (FIFO) out-Strategie 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 g • Statusvariable top 1 23 4 5 6 7 8 Stack S 161410 8 7 topp = 5 class Stack { private int top; private p ate int t S[]; /* constructor, methods push, pop */ } 7 Stapel • Beispiel push(4); push(6); pop; push(8); pop; pop; 1 23 4 5 6 7 8 Stack S 161410 8 7 top = 5 8 Anwendung • Verwaltung von Methodenaufrufen – Speichert Rücksprungadresse und lokale Variablen • Aufruf einer Methode – P Push: h Rü Rücksprungadresse k d – Push: Speicherplatz für lokale Variablen der aufgerufenen Methode • Ende der Methode – Pop: lokale Variablen – Pop: Rücksprungadresse 9 Anwendung Stack H H; void id H { …; e(); …; } void id e { …; l(); …; W(); …; } void id l() { …; o(); …; } 10 Stack overflow • Zu tiefe Verschachtelung von Aufrufen • Zu hohe Rekursionstiefe 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 g eines Stacks vermieden werden • Vorteile – Kl Kleinerer i Aufwand A f d – 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()) ( p y()) { left = leftStack.pop(); right = rightStack.pop(); if(left<right) { int p = partition(array, left, right); leftStack push(left); rightStack leftStack.push(left); rightStack.push(p-1); 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 p mit anderen Datenstrukturen möglich g (z.B. verkettete Listen) • Statusvariable kopf, ende – ende zeigt auf erstes freies Element Queue Q 1 23 4 5 6 7 8 10 8 7 1614 kopf p =3 ende = 8 class Q Queue { private int kopf, ende; private int Q[]; /* constructor, methods queue, dequeue */ } 13 Warteschlange • Beispiel queue(6); queue(9); queue(11); dequeue; dequeue Queue Q 1 23 4 5 6 7 8 10 8 7 1614 kopf = 3 ende = 8 14 Anwendungen • Verwaltung von Aufträgen, die mittels einer first-come, 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 y • 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 D Doppelt l verkettete k Li Liste Zyklische Liste (Sortierte S 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: E d null ll Zeiger Z i class LinkedList { private class ListElement { ListElement prev, next; i int k key; } ListElement head; /* methods to add elements, etc. */ } 18 Durchsuchen ListElement Search(int k) { ListElement cur = head; while(cur!=null hil ( ! ll && k! 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.prev head = e; e.prev = null; } 20 Entfernen • Doppelt verkettete Liste void id d delete(ListElement l t (Li tEl t d) { 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, il ersetzt head h d • 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 il = 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 p Zeiger g und Objekte j mit Feldern 25 Zeiger und Objekte mit Feldern • Liste head 9 16 4 1 • Mehrfelddarstellung 1 2 3 4 5 6 7 8 next key prev • Einfelddarstellung 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 16 17 1 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 k key prev 27 Allokieren und Freigeben x = allocateObject(); insert(x); key[x] = 25; delete(5); 1 2 3 4 5 6 7 8 next y key prev 1 2 3 4 5 6 7 8 next y key prev 1 2 3 4 5 6 7 8 next y 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; i t key; int k } 31 Bäume mit unbeschränktem Grad class Node { Node parent, firstChild, nextSibling; i int k 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 Fi d alle ll N Nachbarn hb iin einem i gewissen i R Radius di • Naiver Ansatz erlaubt Abfragen g 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 A d – Künstliche Intelligenz Intelligenz, Klassifikation http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm http://en.wikipedia.org/wiki/Nearest_neighbor_search – Computergrafik, C t fik Ray R T Tracing i 35 Konstruktion • kd-Baum teilt Raum rekursiv entlang einer SplitEbene in zwei Halbräume • Jeder Knoten – Speichert p 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 p , im rechten Teilbaum „über“ der Splitp Split-Ebene, Ebene • Siehe auch http://en.wikipedia.org/wiki/Kd-tree p p g 36 Beispiel Menge g von Punkten in 2D kd-Baum 37 Konstruktion • Balancierter kd-Baum – axis i ist i t eine i Koordinatenachse K di t h (x ( oder d y iin 2D) – cycle(axis) rotiert durch die Koordinatenachsen: h cycle(x) = y, cycle(y) = x node d constructKdTree t tKdT ( (set t of f points, i t axis) i ) 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 g 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, kd Baum der am nächsten beim Abfragepunkt liegt • Ablauf – Traversiere kd Baum bis zu Blatt Blatt, in das der Abfragepunkt fällt – Blattknoten latt ote ist st Kandidat a d dat für ü nächster äc ste Nac Nachbar ba – 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, 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