Wiederholung ADT Menge: I Sorten: Bool, Element, Menge I Signatur: leer : Menge → leeremenge : add : Menge × Element → remove : Menge × Element → contains : Menge × Element → Bool Menge Menge Menge Bool geignete Datenstrukturen: I Liste (evtl. sortiert, einfach oder doppelt verkettet) Laufzeiten O(n) I Suchbäume (evtl. balanciert) Laufzeiten O(log(n)) meist Speichern von Paaren (Schlüssel, Wert) 141 Hashing Speichern von Paaren (Wert, Adresse) Menge aller Werte: D Hashtabelle (Speicher): m Behälter (Buckets) Menge aller Adressen: {0, . . . , m − 1} Idee: Berechnung eines Behälter-Index (Adresse) durch eine Hashfunktion H : D → {0, . . . , m − 1} Zugriff auf Wert v über Adresse h(v ) 142 Hashfunktion erwünschte Eigenschaften: I effizient berechenbar I surjektiv (jede Adresse als Funktionswert eines Wertes) I möglichst gleichmäßige Verteilung der Werte auf den Adressbereich Beispiel: 7 Wochentage (zwei Anfangsbuchstaben) auf 10 Adressen verteilen: h(v ) = (Summe der Buchstabenindizes) mod 10 0 1 2 3 4 5 6 7 8 9 SA MI DI FR,SO MO DO übliche Hashfunktionen: I h(v ) = v mod m I h(v ) = mittlere Ziffern von v 2 143 Kollisionen Kollision: Werte u 6= v mit h(u) = h(v ) Kollisionen kommen relativ häufig vor. Geburtstagsparadoxon: Unter 23 Personen haben mit 50% Wahrscheinlichkeit denselben Geburtstag. Kollisionsbehandlung: offenes Hashing: Speichern mehrerer Werte in einem Behälter geeignet bei häufigem Einfügen und Löschen geschlossenes Hashing: systematische Berechnung neuer Adressen bei belegtem Behälter geeignet bei bekannter Anzahl zu speichernder Werte (günstig bis 80% der Arraygröße) 144 Offenes Hashing (seperate chaining) Behälter: Liste Hashtabelle: Array fester Länge m von Listen Operationen: Suche eines Wertes v : Suche in der Liste im Behälter h(v ) Löschen eines Wertes v : Löschen aus der Liste im Behälter h(v ) Einfügen eines Wertes v : Einfügen in die Liste im Behälter h(v ) Länge der Listen bei m Behältern und n Werten durchschnittlich n/m. Durchschnittliche Laufzeit von Suche, Einfügen, Löschen (für n ≈ m): O(1 + n/m) = O(1) worst case: alle Werte in einer Liste Laufzeit von Suche, Einfügen, Löschen: O(1 + n) = O(n) 145 Geschlossenes Hashing I I Behälter: Speicher für genau einen Wert zwei zusätzliche Werte: frei, gelöscht Hashtabelle: Array fester Länge m von Werten Idee: I Jedem Wert v wird ein Sondierungspfad s (Liste von Adressen) zugeordnet. I Zum Speichern von v wird s bis zur erste freien Adresse durchlaufen. Invariante für Hashtabelle T : 1. T enthält wenigstens einen leeren Behälter. 2. Für Sondierungspfad s eines Elementes v gilt: Ist v in T enthalten, dann I I steht v an einer Position si des Sondierungspfades von v und alle Positionen j < i auf dem Sondierungspfad S sind nicht frei. 146 Operationen bei geschlossenem Hashing Suchen, Löschen, Einfügen eines Wertes v in die Hashtabelle T: Durchlauf des Sondierungspfades s von v I Suche eines Wertes v in T : I I I v ist genau dann in T enthalten, falls v an einer Adresse des Sondierungspfades gespeichert ist. Durchlauf des Sondierungspfades genügt nach Invariante. Suche erfolgreich, falls v auf s gefunden. Suche erfolglos, falls erste freie Adresse auf s gefunden. I Einfügen eines Wertes v in T : an der ersten Adresse mit Wert „frei“ oder „gelöscht“ auf dem Sondierungspfad s von v I Löschen eines Wertes v aus T : Suche nach dem Wert v auf dem Sondierungspfad s von v, falls gefunden, überschreiben mit Wert „gelöscht“ Warum? 147 Wahl der Sondierungsfunktion N Sondierungsfunktion s : × D → {0, . . . , m − 1} zu Berechnung des Sondierungspfades für einen Wert v s = (s1 (v ), s2 (v ), . . .) erwünschte Eigenschaften: I effizient zu berechnen, I für jeden Wert v überdeckt der Sondierungspfad s alle Behälter I möglichst gleichmäßige Verteilung der Werte über die Hashtabelle (Cluster vermeiden) übliche Verfahren: I lineares Sondieren: si = (h(v ) + i) mod m Modifikation si = (h(v ) + ci) mod m I quadratisches Sondieren: si+1 = (h(v ) + i 2 ) mod m I doppeltes Hashing si+1 = (h(v ) + ih0 (x)) mod m mit zweiter Hashfunktion h0 148 Lineares Sondieren Sondierungsfunktionen si (v ) = (h(v ) + i) mod m Beispiel: m = 13, h(v ) = v mod 13 Einfügen von 18, 41, 22, 44, 59, 32, 31, 73 I Vorteile: schnell, einfach I Nachteile: Clusterbildung Verallgemeinerung: Hashfunktionen si (v ) = (h(v ) + ci) mod m 149 Quadratisches Sondieren Sondierungsfunktionen si (v ) = (h(v ) + i 2 ) mod m Beispiel: m = 13, h(v ) = v mod 13 Einfügen von 18, 41, 22, 44, 59, 32, 31, 73 I Vorteile: vermeidet Clusterbildung I Nachteile: Sondierungsfolge nur halb so lang wie Hashtabelle I überdeckt für Primzahl-Modul Verallgemeinerung: Hashfunktionen s2i−1 (v ) = (h(v ) + i 2 ) mod m s2i (v ) = (h(v ) − i 2 + m2 ) mod m 150 Doppeltes Hashing voneinander unabhängige Hashfunktionen h, h0 definieren die Folge von Positionen: si (v ) = (h(v ) + ih0 (v )) mod m Beispiel: m = 13, h(v ) = v mod 13, h0 (v ) = 7 − v mod 7 Einfügen von 18, 41, 22, 44, 59, 32, 31, 73 lineares Hashing ist ein einfacher Spezialfall. 151 Laufzeit der Operationen Worst-Case für alle Hashverfahren für Suche, Einfügen, Löschen: O(n) Average-Case für gute Hashverfahren für Suche, Einfügen, Löschen: O(1) 152 Datenstrukturen für Mengen I Arrays, Listen I binäre Suchbäume I AVL-Bäume I Hashtabellen 153