Kapitel 9 Suchalgorithmen Suchverfahren: Verfahren, das in einem Suchraum nach Mustern oder Objekten mit bestimmten Eigenschaften sucht. Vielfältige Anwendungsbereiche für Suchverfahren: u.a. Suchen in Datenbanken, Google-Search, DNA-Tests Suchen nach ähnlichen Mustern: z.b. Viren Bilderkennungsverfahren: Suchen nach Pattern Ziel von Kapitel 9: Einfache Suchverfahren Basis: Elementare Datenstrukturen: Listen Effiziente Datenstrukturen: Bäume AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 1 Verschiedene Suchalgorithmen angepasst an die Anforderungen: 1. Statische, kleine Menge, selten Suchoperation notwendig: Lösung: Feld als Datenstruktur und sequentielles Suchen: O(n) 2. Statische, kleine Menge, häufige Zugriffe/Suchoperationen Lösung: Vorsortiertes O(n log n) Feld, binäres Suchen O(log n) 3. Dynamisch, große Menge von Elementen, z.b. Personaldaten Lösung: Baum als dynamische Datenstruktur (einfügen, löschen), organisiert als binärer Suchbaum O(h), h ist Baumhöhe Worst-Case: h = n, Best-Case: h= log n (balanciert) 4. Dynamisch, große Menge, viele, effiziente Zugriffe notwendig z.b. große Produktdatenbanken, Suchmaschinen, Lösung: Binärer Suchbaum, der eine möglichst geringe Höhe h garantiert: z.b. Rot-Schwarz Baum AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 2 Suchalgorithmen analog zum Sortieren: Charakterisierung der gesuchten Objekte durch Such-Schlüssel. Such-Schlüssel können z.b. Attribute der Objekte sein. Beispiel: Versicherungsnummer von Personen 9.1 Lineare Suche Gegeben sei ein Feld A[1.. n] von (ggf. auch unsortierten) Datenelementen und ein Element x mit Such-Schlüssel k. Idee der linearen Suche: sequentielles Durchlaufen des Feldes A und Vergleich der Werte A[i], i=1,, n, mit dem Such-Schlüssel k. Linear Search (A, k) // Ausgabe: A[i], mit A[i]=k oder 0, falls k nicht in A 1. i = 1 2. while (A[i] k) and (i n) do 3. i= i+1 4. if i n then return A[i] 5. else return 0 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 3 Laufzeitanalyse: Best-Case: sofortiger Treffer: T(n) = 1, also T(n) = O(1) Worst-Case: alles durchsuchen: T(n) = n, also T(n) = O(n) Average-Case: erfolgreiche Suche unter der Annahme, dass jede Anordnung der Elemente gleich wahrscheinlich ist: T(n) = also T(n) = O(n) Fazit: sehr einfaches Verfahren, eignet sich auch für einfach verkettete Listen, das Verfahren ist auch für unsortierte Felder geeignet, aber das Verfahren ist nur für kleine Werte von n praktikabel. AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 4
9.2 Binäre Suche Falls in einer Folge häufig gesucht werden muss, so lohnt es sich, die Feldelemente sortiert zu speichern. Vorgehen: Eingabe: Sortiertes Feld A halbieren des Suchbereichs in jedem Schritt, indem der gesuchte Wert k mit dem Wert auf der Mittelposition des geordneten Feldes verglichen wird. Fall 1: Gesuchter Wert ist kleiner: weiterarbeiten mit linkem Teilfeld. Fall 2: Gesuchter Wert ist größer: weiterarbeiten mit rechtem Teilfeld. Eingabe: Feld A[1.. n], Schlüssel s Ausgabe: Position von s oder 0, falls s nicht vorhanden BINARY-SEARCH(A, s) 1 l = 1 2 r = n 3 m = (l + r) / 2 4 while (s A[m] and l r) 5 if s < A[m] 6 then r = m 1 7 else l = m + 1 8 m = (l + r) / 2 8 if s = A[m] 9 then return m 10 else return 0 Beispiel: Eingabe: Sortiertes Feld A = (5, 15, 20, 22, 37) AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 5 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 6 Binary Search Algorithm C Code Binary Search Algorithm C Code int BinarySearch(int *array, int key, int low, int high) int middle; if(low > high) /* termination case */ return -1; middle = (low+high)/2; if(array[middle] == key) return middle; else if(array[middle] > key) return BinarySearch(array, key, low, middle-1); /* search left */ return BinarySearch(array, key, middle+1, high); /* search right */ AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 7 #include <stdbool.h> typedef unsigned long ulong; typedef int (*CompareProc) /* compares an element key against the desired key */ ( ulong Element, void * Arg ); /* result should be -1, 0 or +1, indicating whether given key is less than, equal to or greater than desired key. */ bool Search ( ulong LowBound, ulong HighBound, CompareProc Compare, void * CompareArg, bool ReturnGreater, ulong * Location ) AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 8
Binary Search Algorithm C Code ulong MidElement; bool Found; int Comparison; for (;;) if (LowBound > HighBound) Found = false; if (ReturnGreater) *Location = LowBound; else *Location = HighBound; /*if*/ break; /*if*/ MidElement = (LowBound + HighBound) / 2; Comparison = Compare(MidElement, CompareArg); if (Comparison < 0) LowBound = MidElement + 1; else if (Comparison > 0) HighBound = MidElement - 1; else *Location = MidElement; Found = true; break; /*if*/ /*for*/ return Found; /*Search*/ Laufzeitanalyse: Zählen der Anzahl der Vergleiche Best-Case: sofortiger Treffer: T(n) = 1, also T(n) = O(1) Worst-Case: Suchbereich muss solange halbiert werden, bis er nur noch 1 Element enthält, also im schlechtesten Fall logarithmisch oft : T(n) = T(n/2) + 1 = log(n + 1), T(n) = Θ(log n) AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 9 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 10 Laufzeitanalyse: Zählen der Anzahl der Vergleiche Average-Case: es gilt T(n) = = Θ(log n) Fazit: Binäre Suche gut geeignet für große Werte n Beispiel: sei n = 2 Millionen Lineare Suche benötigt im Worst Case 2 Millionen Vergleiche Binäre Suche benötigt: log (2 * 10 6 ) 20 Vergleiche nicht gut geeignet, wenn sich die Daten häufig ändern (Dynamik), dann ist das Suchen in binären Suchbäumen (s.u.) besser geeignet. 9.3 Suchen in binären Suchbäumen Erinnerung: Kapitel 7 Baumstrukturen Binärer Suchbaum ist ein Binärbaum und für jeden Knoten v gilt: die Schlüssel der Knoten des linken Teilbaums von v sind kleiner oder gleich k und die Schlüssel der Knoten des rechten Teilbaums von v sind größer als k. AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 11 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 12
Eigenschaften binärer Suchbäume Operationen auf dem Baum, wie Suchen, Einfügen, Entnehmen, sind in O(h) Schritten ausführbar, wobei h die Höhe des Baumes ist. Repräsentation eines binären Suchbaumes: als eine verkettete Liste. Jeder Knoten v des Baumes besitzt mindestens die Attribute: schlüssel[v], links[v], rechts[v], p[v], das sind Verweise auf das linke und Beispiel rechte Kind, sowie auf den Vaterknoten Binärer Suchbäume Bem.: Unterschiedliche binäre Suchbäume können die gleiche Menge von Werten repräsentieren. Beispiel: Eingabe: Werte 2, 3, 5, 7, 8 Fall (a) Suchbaum mit 6 Knoten der Höhe h = 2 Fall (b) Suchbaum mit gleichen Schlüssel, der weniger effizient organisiert ist und die Höhe h = 4 besitzt. Fall (a) Fall (b) AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 13 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 14 9.3.1 Suchen Aufgabe: Suche nach einem Schlüssel k in dem Baum. Lösungsansatz: durch Rekursion wird ein Pfad von der Wurzel zum gesuchten Knoten beschrieben, d.h. Laufzeit von Tree-Search ist O(h), h ist die Baumhöhe. TREE-SEARCH(x, k) //x ist Hilfszeiger, verweist auf die Wurzel des Baumes 1 if x == NULL oder k == schlüssel[x] 2 then return x 3 if k < schlüssel[x] 4 then return TREE-SEARCH(links[x], k) 5 else return TREE SEARCH(rechts[x], k) Beispiel: Suche nach k=13 Start: schlüssel[x] = 15 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 15 Häufig ist eine rekursiv programmierte Suche auf Rechnern nicht so effizient ausführbar wie eine iterativ programmierte Suche Iterative Beschreibung der Suche: ITERATIVE-TREE-SEARCH(x, k) 1 while x NULL und k schlüssel[x] 2 if k < schlüssel[x] 3 then x = links[x] 4 else x = rechts[x] 5 return x 9.3.2 Minimum und Maximum Suche Eine häufig benötigte Operation ist die Suche nach dem kleinsten bzw. größten Element des Baumes. AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 16
Suche nach kleinstem Element: ist wegen der binären Suchbaum-Eigenschaft sehr einfach: gehe von der Wurzel immer den linken Teilbaum hinunter: Tree-Minimum gibt einen Zeiger auf das Element x mit dem kleinsten Schlüssel zurück. Terminierung, wenn der linke Teilbaum eines Knotens x NULL ist Beispiel TREE-MINIMUM (x) 1 while links[x] NULL 2 x = links[x] 3 return x Suche nach größtem Element: analoges Vorgehen gehe von der Wurzel immer den rechten Teilbaum hinunter: Tree-Maximum gibt einen Zeiger auf das Element x mit dem größten Schlüssel zurück. Terminierung, wenn der rechte Teilbaum eines Knotens x NULL ist TREE-MAXIMUM(x) 1 while rechts[x] NULL 2 x = rechts[x] 3 return x Beispiel-Baum auf Folie 15: maximales Element: x = 20 Laufzeit: Tree-Minimum und Tree-Maximum benötigen für einen Baum mit Höhe h eine Laufzeit von O(h); Berechnung baut einen Pfad von der Wurzel bis zum Blatt entlang linker bzw. rechter Äste auf. AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 17 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 18 9.3.3 Nachfolgerknoten eines Knotens x Seien die Schlüssel der Knoten paarweise verschieden, dann gilt: Nachfolger von x ist der Knoten y mit: schlüssel[x] < schlüssel[y] und für alle Knoten r y, mit schlüssel[x] < schlüssel[r] gilt: schlüssel[y] < schlüssel[r] 1 if rechts[x] NIL 2 then return TREE-MINIMUM(rechts[x]) 3 y = p[x] 4 while y NIL und x = rechts[y] 5 x = y 6 y = p[y] 7return y Beispiel mit schlüssel[x] = 15 1 if rechts[x] NIL 2 then return TREE-MINIMUM(rechts[x]) 3 y = p[x] 4 while y NIL und x = rechts[y] 5 x = y 6 y = p[y] 7 return y AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 19 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 20
9.3.4 Einfügen und Löschen in binärem Suchbaum Ziel: Aufrechterhalten der binären Suchbaum-Eigenschaft! Einfügen Einzufügen sei ein Knoten z, mit schlüssel[z] = v, links[z] = NULL, rechts[z] = NULL Finden der korrekten Position TREE-INSERT(T, z) 1 y = NULL 2 x = wurzel[t] 3 while x NULL 4 y = x 5 if schlüssel[z] < schlüssel[x] 6 then x = links[x] 7 else x = rechts[x] 8 p[z] = y 9 if y = NULL 10 then wurzel[t] = z 11 else if schlüssel [z] < schlüssel[y] 12 then links[y] = z 13 else rechts[y] = z Beispiel: Einfügen eines Knotens z mit schlüssel[z] = 13 Hell schattierte Knoten in Abb.: Pfad von der Wurzel bis zur korrekten Position Gestrichelte Linie: neue Verbindung, um das Element z einzufügen Tree-Insert startet bei der Wurzel: Zeiger x verfolgt den Pfad, Zeiger y verweist auf den Vater von x, In der while-schleife wandern beide Zeiger im Baum nach unten. Wenn x auf NULL verweist: Position für das Einfügen ist gefunden. Anweisungen in den Zeilen 8-13 fügen Knoten z ein. AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 21 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 22 Einfügen ist somit auch eine einfache Operation auf Suchbäumen. Die Laufzeit von Tree-Insert ist ebenfalls O(h), h ist die Baumhöhe. Löschen Vgl. nächste Folie, dort ist das Vorgehen veranschaulicht. 1 if rechts[x] NULL 2 then return TREE-MINIMUM(rechts[x]) 3 y = p[x] 4 while y NULL und x = rechts[y] 5 x = y 6 y = p[y] 7 return y AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 23 Beispiel: die drei Fälle des Tree-Delete Fall (a) z hat keine Kinder AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 24
Beispiel: (Forts.) Fall (b) z hat nur ein Kind Beispiel: (Forts.) Fall (c) z hat nur ein Kind AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 25 AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 26 Bem.: Die Operationen auf binären Suchbäumen haben alle eine Laufzeit, die proportional zu Höhe des Baumes ist. Binäre Suchbäume können aber degeneriert sein, d.h. bei einem Baum mit n Knoten kann im Worst-Case die Laufzeit Θ(n) sein Ziel: Strukturieren von Suchbäumen so, dass Höhe h für gute Performance sorgt! Lösung: Konstruktion von balancierten Suchbäumen, so dass gilt: Der Baum hat eine garantierte kleine Höhe und die Laufzeit ist O(lg n). B-Bäume, AVL-, oder Rot-Schwarz-Bäume sind hierfür geeignet AuD, WS11/12, C. Eckert & Th. Stibor, Kapitel 9 Suchalgorithmen 27