Übersicht Elementare Datenstrukturen • Stapel & Warteschlangen • Verkettete Listen Datenstrukturen & Algorithmen • Bäume Matthias Zwicker Universität Bern Frühling 2009 2 Dynamische Mengen Dynamische Mengen • Daten speichern und abfragen • Mögliche Operationen Dynamische Menge S, Schlüssel k, Referenz auf Record x • Records aus Schlüssel und Satellitendaten class Record { int key; SatelliteData data; } – Search(S,k) – Insert(S,x) Insert(S x) – Delete(S,x) – Minimum(S) – Maximum(S) – Successor(S,x) – Predecessor(S,x) class SatelliteData{ String g name; ; String address; ... } 3 4 Datenstrukturen Stapel & Warteschlangen • Geeignete Datenstruktur je nach Operationen, die effizient unterstützt werden soll • Dynamische Mengen mit effizientem Einfügen und Entfernen in O(1) – Insert(S,x) – x = Remove(S) • Zeitkomplexität wird als Funktion der Grösse n der Menge gemessen Stapel • Einfügen und Entfernen mit last-in-, firstout-Strategie (LIFO) Warteschlangen • Einfügen und Entfernen mit first-in-, firstout-Strategie (FIFO) 5 6 1 Stapel (LIFO) Stapel • Einfügen = push, Entfernen = pop • Beispiel push(4); push(6); pop; push(8); pop; pop; • Hier: Implementation mit Feld – Implementation mit anderen Datenstrukturen möglich – Elemente hier Integers der Einfachkeit halber, g Records mit Satellitendaten funktioniern gleich 1 23 4 5 6 7 8 Stack S 161410 8 7 top = 5 • Statusvariable top 1 23 4 5 6 7 8 Stack S 161410 8 7 top = 5 class Stack { private int top; private int S[]; /* constructor, methods push, pop */ } 7 8 Anwendung Warteschlange (FIFO) • Nicht-rekursiver QuickSort mit Stack • Einfügen = queue, Entfernen = dequeue • Hier: Implementation mit Feld 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); } } } – Implementation mit anderen Datenstrukturen möglich • Statusvariable S i bl kopf, k f ende d Queue Q 1 23 4 5 6 7 8 10 8 7 1614 kopf = 3 ende = 7 class Queue { private int kopf, ende; private int Q[]; /* constructor, methods queue, dequeue */ } 9 10 Warteschlange Anwendungen • Beispiel • Verwaltung von Aufträgen, die mittels einer first-come, first-serve Strategie bearbeitet werden sollen queue(6); queue(9); queue(11); dequeue; dequeue Queue Q 1 23 4 5 6 7 8 10 8 7 1614 kopf = 3 ende = 7 11 12 2 Verkettete Listen Verkettete Listen • Vorteil gegenüber Feldern: maximale Anzahl Elemente ist dynamisch • Listenelemente bestehen aus – Schlüssel (oder Referenz auf andere Daten) – Zeiger auf nachfolgendes Listenelement – Doppelt verkettet: Zeiger auf vorheriges Listenelement • Varianten – – – – Einfach verkettete Liste Doppelt verkettete Liste Zyklische Liste Sortierte Liste • Zusätzlich: Zeiger kopf auf erstes Listenelement • Beispiel class LinkedList { private class ListElement { ListElement prev, next; int key; } ListElement head; /* methods to add elements, etc. */ } 13 Durchsuchen 14 Einfügen • Doppelt verkettete Liste ListElement Search(int k) { ListElement cur = head; while(head!=null && k!=cur.key) cur = cur.next; return cur; } void insert(ListElement e) { e.next = head; if(head!=null) head.prev = e; head = e; e.prev = null; } 15 Entfernen 16 Wächter • Dummy Objekt, um Randbedingungen zu vereinfachen • Doppelt verkettete Liste void delete(ListElement d) { if(d.prev!=null) d.prev.next = d.next; else head = d.next; if(d.next!=null) d.next.prev = d.prev; } • Verknüpft Liste zu einem Ring (zyklische Liste) • Zeiger auf Dummy bezeichet mit nil, ersetzt kopf • Hier doppelt kerkettete Liste class CyclicList { private class ListElement { ListElement prev, next; int key; } ListElement nil; /* methods to add elements, etc. */ } 17 18 3 Einfügen in Liste mit Wächter Parameterisierte Klasse in Java class LinkedList<T> { private class Element<T> { private Element prev, next; private T data; } private Element<T> nil; LinkedList() () { nil = new T(); nil.next = nil; nil.prev = nil; } void insert(T e) {...} /* add other operations */ } void insertElement(ListElement e) { e.next = nil.next; nil.next.prev = e; nil.next = e; e.prev = nil; } 19 20 Zeiger und Objekte mit Feldern Zeiger und Objekte mit Feldern • Was, wenn eine Programmiersprache keine Zeiger zur Verfügung stellt? • Liste kopf 9 16 4 1 • Mehrfelddarstellung • Implementiere Zeiger und Objekte mit Feldern 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 16 17 18 19 20 21 22 23 24 21 22 Allokieren und Freigeben von Objekten Allokieren und Freigeben • Wollen Operationen auf dynamischen Listen anbieten wie vorher x = allocateObject(); insert(x); – Insbesondere, allokieren und einfügen neuer Objekte – Allokieren ähnlich „new()“ in Java – Brauchen Zugriff auf freie Elemente in Felder key[x] = 25; delete(5); • Unterhalte zwei Listen 1 2 3 4 5 6 7 8 – Liste mit Daten – Liste mit freien Objekten next key prev 1 2 3 4 5 6 7 8 next key prev 1 2 3 4 5 6 7 8 next key prev next key prev 23 24 4 Gerichtete Bäume Binäre 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 25 Bäume mit unbeschränktem Grad 26 kd-Bäume • Räumliche Datenstruktur • Speichert Position von Punkten im ddimensionalen Raum • Erlaubt effiziente Abfragen wie – Fi Finde d nächsten ä ht N Nachbarn hb – Finde k-nächste Nachbarn – Finde alle Nachbarn in einem gewissen Radius • Anwendungen – Künstliche Intelligenz – Computergrafik 27 Konstruktion 28 Konstruktion • Viele Varianten, diese hier führt zu einem balancierten kd-Baum node constructKdTree (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 } } Menge von Punkten in 2D 29 kd-Baum 30 5 Einfügen 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 Neuer Punkt (3,5) 31 (3,5) 32 Nächster Nachbar • Input: Neuer Punkt • Output: nächster Punkt im kd Baum • Ablauf – Traversiere kd Baum bis zu Blatt, in das der Abfragepunkt fällt – Blattknoten ist Kandidat 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 33 6