if - an der HTWG Konstanz

Werbung
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-1
Dictionaries (1)
Dictionary
Datenstruktur zur effizienten Verwaltung einer (dynamischen) Menge
von Datensätzen.
Datensätze (records)
bestehen aus
!  Suchschlüssel (search key) und
!  Nutzdaten (value).
Beispiel Telefonbuch:
Beispiel Wörterbuch:
Maier
Hauptstr. 1
14234
sehen
see; look
Baier
Bahnhofstr. 5
24829
sprechen
speak; talk
Müller
Uferweg 5
53289
gehen
go; walk
Anton
Mozartstr. 2
36478
hören
hear; listen
Suchschlüssel
Datensatz
Nutzdaten: Adresse + Tel.Nr
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-2
Dictionaries (2)
Operationen:
!  search(key)
suche nach Datensatz mit Schlüssel key.
!  insert(key, value)
füge neuen Datensatz mit Schlüssel key und Daten value ein.
!  remove(key)
Lösche Datensatz mit Schlüssel key.
Datenstrukturen mit diesen Operationen werden in der Literatur und
Programmiersprachen auch als Dictionaries oder Maps bezeichnet.
Eventuell zusätzliche Operationen:
!  Traversieren:
Gehe über alle Datensätze in bestimmter Reihenfolge:
-  über Schlüssel sortiert
-  nach Einfügezeitpunkt
-  irgendeine Reihenfolge (von der Implementierung bestimmt)
!  Union:
Vereinige zwei Dictionaries.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-3
Varianten
Eindeutigkeit der Schlüssel:
!  Die Zuordnung von Schlüssel zu einem Datensatz ist meistens eindeutig.
!  Es können aber auch mehrere Datensätze zu einem Schlüssel existieren.
Die search-Operation liefert dann mehrere Datensätze zu einem Schlüssel zurück.
!  Die Forderung der Eindeutigkeit der Schlüssel hängt von der Anwendung ab.
Beispiele:
-  Telefonbuch: Schlüssel (= Familenname) sind nicht eindeutig
-  Wörterbuch: Schlüssel sind eindeutig
Schlüsseltypen:
!  int,
!  String,
!  zusammengesetze Daten; z.B. Uhrzeit, Kalenderdatum, ...
Dictionaries ohne Nutzdaten:
!  entsprechen Mengen von Schlüssel und werden auch Sets genannt.
In dieser Vorlesung:
!  Schlüssel sind eindeutig.
!  Im wesentlichen nur int-Werte als Schlüssel.
Nutzdaten in den Darstellungen meist weggelassen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-4
Dictionaries in Programmbibliotheken
Üblicherweise als generische Klasse:
!  Schlüsseltyp und Typ für Nutzdaten werden parameterisiert
!  Z.B. K für Key und V für Value.
Java:
!  Interface Set<K> mit den Implementierungen
-  TreeSet<K> (ausgeglichener binärer Suchbaum)
-  und HashSet<K> (Hashverfahren)
!  Interface Map<K, V> mit den Implementierungen TreeMap<K, V> und
HashMap<K, V>
!  Bei TreeSet<K> und TreeMap<K, V> muss der Schlüsseltyp K den
Vergleichsoperator compareTo (siehe Interface Comparable) anbieten.
!  Dictionary<K, V> ist veraltet; stattdessen Map<K, V> verwenden.
STL in C++:
!  Template-Klassen set<K> und map<K, V>
!  Template-Klassen multiset<K> und multimap<K, V>, falls Schlüssel mehrfach
vorkommen dürfen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-5
Teil I: Schlüsselbasiertes Suchen
!  Dictionaries
!  Elementare Suchverfahren
-  Sequentielle Suche
-  Binäre Suche
-  Umsetzung in Java
! 
! 
! 
! 
! 
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-6
Sequentielle Suche (1)
Datenstruktur
!  Halte n Datensätzen in einem Feld lückenlos und unsortiert.
!  Alternative: linear verkettete Liste.
7 3
a[0] [1]
9
...
5
...
[n-1]
Algorithmen (in Pseudocode)
search(int k) {
for (int i = 0; i < n; i++)
if (a[i].key == k)
breche ab, da k gefunden;
nicht gefunden;
}
remove(int k) {
if (k kommt in a vor) {
lösche k und schließe Lücke;
n--;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
insert(int k) {
if (k kommt nicht in a vor) {
if (a vollständig gefüllt)
vergrößere Feld a;
a[n++] = k;
}
}
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-7
Sequentielle Suche (2)
Laufzeiten (Worst Case):
Operation bei einer Menge
mit n Elementen
T(n)
search
O(n)
insert
O(n) *)
remove
O(n)
Aufbau einer Menge mit n Elementen
(d.h. n insert-Aufrufe)
O(n2)
n search-Aufrufe
O(n2)
*)
Es muss geprüft werden, ob das
Element bereits vorkommt (search-Aufruf)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-8
Binäre Suche (1)
Datenstruktur
!  Halte Datensätze lückenlos in einem sortierten Feld.
Idee für Such-Algorithmus
Mittleres Element
a[m]
1
3
5
6
8
9
11
12 15 17 19 20 22
Sortiertes Feld a
k < a[m] oder k > a[m] oder k == a[m] ?
Falls k == a[m], dann gefunden.
Falls k < a[m],
dann suche in linker Hälfte weiter.
Falls k > a[m],
dann suche in rechter Hälfte weiter
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-9
Binäre Suche (2)
Beispiel:
Suche nach k = 8:
li=0
1
m=6
3
li=0
1
5
6
8
5
11
12 15 17 19 20 22
11
12 15 17 19 20 22
Suche in linker Hälfte
11
12 15 17 19 20 22
Suche in rechter Hälfte;
k wird gefunden!
re=5
m=2
3
9
re=12
6
8
9
li=3 m=4 re=5
1
3
5
6
8
9
Zu durchsuchender Bereich geht von a[li] bis a[re]
Mittleres Element m = (li + re)/2 (ganzzahlige Division)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-10
Binäre Suche (3)
Algorithmen:
search(int k) {
int li = 0;
int re = n-1;
Laufzeit von search:
T(n) = O(log n)
In jedem Schleifendurchlauf wird
while (re >= li) {
das zu durchsuchende Teilfeld in
int m = (li + re)/2;
etwa halbiert.
if (k == a[m].key)
breche ab, da k gefunden;
else if (k < a[m].key)
re = m – 1;
Suche weiter in linker Hälfte
else
li = m + 1;
Suche weiter in rechter Hälfte
}
k wurde nicht gefunden;
}
insert(int k)
// füge in sortiertes Feld ein
Prof. Dr. O. Bittel, HTWG Konstanz
remove(int k)
// wie zuvor
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-11
Binäre Suche (4)
Laufzeiten (Worst Case):
Operation bei einer Menge
mit n Elementen
T(n)
search
O(log n)
insert
O(n)
remove
O(n)
Aufbau einer Menge mit n Elementen
(d.h. n insert-Aufrufe)
O(n2) *)
n search-Aufrufe
O(n log n)
*)
Alternativ lassen sich auch n Elemente unsortiert einfügen und
dann mit einem schnellen Sortierverfahren sortieren.
Damit würde man T(n) = O(n log n) erreichen
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-12
Umsetzung in Java
Konzept:
!  Definiere Dictionary<K, V> als Interface
!  Implementiere Klassen:
-  ArrayDictionary<K, V> mit sequentieller Suche
-  SortedArrayDictionaryK, V> mit binärer Suche
Dictionary<K, V>
+ V search(K);
+ V insert(K, V);
+V remove(K);
ArrayDictionary<K, V>
Prof. Dr. O. Bittel, HTWG Konstanz
SortedArrayDictionary<K, V>
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-13
Interface Dictionary
public interface Dictionary<K,V> {
V insert(K key, V value);
// Falls ein Datensatz mit Schlüssel key noch nicht existiert, wird das
// Schlüssel-Wert-Paar key, value eingefügt und null zurückgeliefert.
// Ansonsten werden beim Datensatz mit Schlüssel key die alten
// Nutzdaten durch value ausgetauscht und zurückgeliefert.
V search(K key);
// Liefert die Daten zu dem Schlüssel key zurück oder
// null falls ein Datensatz mit Schlüssel key nicht vorhanden ist.
V remove(K key);
// Löscht den Datensatz mit Schlüssel key (falls vorhanden) und liefert die alten
// Daten zurück. Falls der Datensatz nicht existiert, wird null zurückgeliefert.
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-14
ArrayDictionary (1)
public class ArrayDictionary<K, V> implements Dictionary<K, V> {
private static class Entry<K,V> {
K key;
V value;
Entry(K k, V v) {key = k; value = v;}
};
private static final int DEF_CAPACITY = 16;
private int size;
private Entry<K,V>[] data;
Feld data mit
@SuppressWarnings("unchecked")
public ArrayDictionary() {
size = 0;
data = new Entry[DEF_CAPACITY];
}
Schlüssel-Wert-Paaren
@SuppressWarnings("unchecked")
private void ensureCapacity(int newCapacity) {
if (newCapacity < size)
return;
Feld data vergrößern.
Entry[] old = data;
data = new Entry[newCapacity];
System.arraycopy(old, 0, data, 0, size);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-15
ArrayDictionary (2)
private int searchKey(K key) {
for (int i = 0; i < size; i++) {
if (data[i].key.equals(key)) {
return i;
}
}
return -1; // nicht gefunden
}
public V search(K key) {
int i = searchKey(key);
if (i >= 0)
return data[i].value;
else
return null;
}
Prof. Dr. O. Bittel, HTWG Konstanz
data[i].key.equals(key) statt
data[i].key == key, denn sonst würde
Vergleich nur auf Referenzebene
stattfinden.
Jedoch muss equals für den Schlüsseltyp
geeignet definiert sein.
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-16
ArrayDictionary (3)
public V insert(K key, V value) {
int i = searchKey(key);
// Vorhandener Eintrag wird überschrieben:
if (i >= 0) {
V r = data[i].value;
data[i].value = value;
return r;
}
// Neueintrag:
if (data.length == size) {
ensureCapacity(2*size);
}
data[size] = new Entry<K,V>(key,value);
size++;
return null;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-17
ArrayDictionary (4)
public V remove(K key) {
int i = searchKey(key);
if (i == -1)
return null;
// Datensatz loeschen und Lücke schließen
V r = data[i].value;
for (int j = i; j < size-1; j++)
data[j] = data[j+1];
data[--size] = null;
return r;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-18
SortedArrayDictionary mit Typbeschränkung (1)
public class SortedArrayDictionary<K extends Comparable<? super K>, V>
implements Dictionary<K, V> {
// ...
!  Typbeschränkung:
Schlüssel von Typ K müssen
vergleichbar sein.
}
public interface Comparable<T> {
int compareTo(T o);
}
!  Dazu genügt es zu fordern, dass die
Elemente von irgendeinem Obertyp
von K vergleichbar sind.
x.compareTo(y) liefert eine negative
Zahl, 0, bzw. eine positive Zahl
zurück, falls x kleiner, gleich bzw.
größer als y ist.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-19
SortedArrayDictionary mit Typbeschränkung (2)
public class SortedArrayDictionary<K extends Comparable<? super K>, V>
implements Dictionary<K, V> {
Daten, Konstruktor, ensureCapacity,
search und remove wie bei
ArrayDictionary
// ...
private int searchKey(K key) {
int li = 0;
int re = size - 1;
Binäre Suche
while (re >= li) {
int m = (li + re)/2;
if (key.compareTo(data[m].key) < 0)
re = m – 1;
else if (key.compareTo(data[m].key) > 0)
li= m + 1;
else
return m; // key gefunden
}
return -1; // key nicht gefunden
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-20
SortedArrayDictionary mit Typbeschränkung (3)
public V insert(K key, V value) {
int i = searchKey(key);
// Vorhandener Eintrag wird überschrieben:
if (i != -1) {
V r = data[i].value;
data[i].value = value;
return r;
}
// Neueintrag:
if (data.length == size) {
ensureCapacity(2*size);
}
int j = size-1;
while (j >= 0 && key.compareTo(data[j].key) < 0) {
data[j+1] = data[j];
Einfuegen in sortiertes Feld.
j--;
}
data[j+1] = new Entry<K,V>(key,value);
size++;
return null;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-21
Anwendung
public class WoerterbuchAnwendung {
public static void main(String[] args) {
Dictionary<String, String> wb;
Scanner in = new Scanner(System.in);
int i;
i = in.nextInt();
if (i == 1)
wb = new ArrayDictionary<String,String>();
else
wb = new SortedArrayDictionary<String, String>();
wb.insert("lesen", "read");
wb.insert("schreiben", "write");
System.out.println(wb.search("lesen"));
}
!  Beachte, dass für den Schlüsseltyp String sowohl equals als auch
compareTo geeignet definiert ist.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-22
Beispiel: Datum als Schlüssel (1)
!  Wie muss die Klasse Datum erweitert werden, so dass Datum als
Schlüsseltyp sowohl für ArrayDictionary als auch SortedArrayDictionary
eingesetzt werden kann?
public class Datum {
private int tag = 1;
private int mon = 1;
private int jahr = 1970;
public Datum() {}
public Datum(int t, int m, int j) {
tag = t; mon = m; jahr = j;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-23
Beispiel: Datum als Schlüssel (2)
public class Datum implements Comparable<Datum> {
private int tag = 1;
private int mon = 1;
private int jahr = 1970;
// ...
public boolean equals(Object o) {
if (this == o) return true;
if (! (o instanceof Datum))) return false;
Datum d = (Datum) o;
return (jahr == d.jahr && mon == d.mon && tag == d.tag);
}
}
public int compareTo(Datum d) {
if (jahr < d.jahr) return -1;
else if (jahr > d.jahr) return 1;
else if (mon < d.mon) return -1;
else if (mon > d.mon) return 1;
else if (tag < d.tag) return -1;
else if (tag > d.tag) return 1;
else return 0;
}
d1.compareTo(d2) liefert
Prof. Dr. O. Bittel, HTWG Konstanz
-1, falls d1 chronologisch vor d2
0, falls d1 gleich d2
+1, falls d1 chronologisch nach d2
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-24
SortedArrayDictionary mit Comparable-Downcast
!  SortedArrayDictionary kann auch ohne Typbeschränkung definiert werden.
Eine compareTo-Methode wird dann durch ein Comparable-Downcast
"erzwungen".
!  Laufzeitfehler (ClassCastException), falls Schlüsseltyp K nicht Comparable ist.
!  Die Java-Klassen TreeSet und TreeMap setzen dieselbe Technik ein.
public class SortedArrayDictionary<K, V> implements Dictionary<K, V> {
// ...
private int searchKey(K key) {
Comparable<? super K> comparableKey
= (Comparable<? super K>) key;
...
!  Es genügt zu fordern,
dass die Elemente von
irgendeinem Obertyp
von K vergleichbar sind.
!  ClassCastException, falls
Cast nicht möglich.
if (comparableKey.compareTo(data[m].key) < 0)
...
}
// ...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-25
SortedArrayDictionary mit Comparator-Parameter (1)
public class SortedArrayDictionary<K, V> implements Dictionary<K, V> {
private Comparator<? super K> cmp;
// ...
public SortedArrayDictionary(Comparator<? super K> c) {
if (c == null)
cmp = ...; // Natural Order als Default-Wert (später)
else
cmp = c;
Für den übergebenen
// ...
Comparator-Parameter c
}
private int searchKey(K key) {
...
if (cmp.compare(key,data[m].key)) < 0)
...
}
// ...
}
public interface Comparator<T> {
int compare(T x, T y);
}
Prof. Dr. O. Bittel, HTWG Konstanz
genügt es zu fordern, dass c
die Elemente von irgendeinem
Obertyp von K vergleichen
kann.
compare(x, y) liefert eine negative Zahl,
0, bzw. eine positive Zahl zurück,
falls x kleiner, gleich bzw. größer als y ist.
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-26
SortedArrayDictionary mit Comparator-Parameter (2)
!  Comparator ist ein funktionales Interface. Daher kann einfachheitshalber ein
Lambda-Ausdruck übergeben werden.
!  Kalenderdaten chronologisch aufsteigend sortiert:
Dictionary<Datum,String> kalender =
new SortedArrayDictionary<>( (d1, d2) -> d1.compareTo(d2) );
Lambda-Ausdruck.
!  Kalenderdaten chronologisch absteigend sortiert:
Dictionary<Datum,String> kalender =
new SortedArrayDictionary<>( (d1, d2) -> d2.compareTo(d1) );
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-27
SortedArrayDictionary mit Comparator-Parameter mit Default-Wert
public class SortedArrayDictionary<K, V> implements Dictionary<K, V> {
private Comparator<? super K> cmp;
// ...
Konstruktor mit
Comprarator-Parameter
public SortedArrayDictionary(Comparator<? super K> c) {
if (c == null)
// Natural Order als Default-Wert:
cmp = (x,y) -> ((Comparable<? super K>) x).compareTo(y);
else
cmp = c;
// ...
}
Default-Konstruktor
public SortedArrayDictionary() {
// Natural Order als Default-Wert:
cmp = (x,y) -> ((Comparable<? super K>) x).compareTo(y);
}
// ...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-28
Teil I: Schlüsselbasiertes Suchen
!  Dictionaries
!  Elementare Suchverfahren
!  Hashverfahren
-  Idee
- 
- 
- 
- 
- 
! 
! 
! 
! 
Hashfunktion
Hashverfahren mit linear verketteten Listen
Offene Hashverfahren
Dynamische Hashverfahren
Hashverfahren in Java
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-29
Idee der Hashverfahren
!  Mit einer Hashfunktion h wird aus einem Schlüssel k
eine Hashadresse h(k) (positive ganze Zahl) berechnet.
!  Die Hashadresse gibt den Index in einem Feld an, wo der Datensatz
abgespeichert werden kann bzw. abgespeichert ist.
!  Das Feld wird auch Hashtabelle genannt.
tab[0]
Schlüssel
k = 17
Hashfunktion h
Hashadresse
h(k) = 3
8
tab[1]
tab[2]
17 tab[3]
11 …
20 tab[6]
Hashtabelle tab
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-30
Wichtige Anforderungen an Hashfunktionen
!  Hashfunktion sollte einfach zu berechnen sein.
!  Gute Streuwirkung: vorkommende Schlüssel sollten
sich möglichst gut über die Tabelle verteilen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-31
Typische Hashfunktionen
!  Division-Rest-Methode
h(k) = k mod m;
Hierbei ist m die Tabellengröße. m sollte möglichst eine Primzahl sein.
!  Multiplikative Methode
h(k) = ⎣ m(kφ-1 – ⎣kφ-1⎦ ) ⎦
–  Dabei ist φ-1 =
des goldenen Schnitts.
≈ 0.6180339887 der Kehrwert
–  ⎣x⎦ rundet auf die nächst kleinere ganze Zahl ab.
–  m ist die Tabellengröße.
–  Beispielsweise bilden die Werte h(1), h(2), …, h(10) für m = 10
eine Permutation der Zahlen 0,1, …, 9 nämlich:
6, 2, 8, 4, 0, 7, 3, 9, 5, 1.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-32
Beispiel mit Division-Rest-Methode
Beispiel
!  Füge die Zahlen 7, 24, 5, 8 in eine Hashtabelle der Größe m = 7 ein.
!  Benutze die Hashfunktion h(k) = k mod m.
7
tab[0]
8
tab[1]
tab[2]
24 tab[3]
tab[4]
5
tab[5]
tab[6]
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-33
Tabellengröße als Primzahl bei Division-Rest-Methode (1)
!  Bei der Division-Rest-Methode ist es zweckmäßig die Tabellengröße als
Primzahl zu wählen.
!  Die beiden folgenden Beispiele zeigen, dass eine ungeschickte Wahl der
Tabellengröße eine schlecht streuende Hashfunktion verursachen kann.
!  Beispiel 1:
-  Die Personaldaten einer Firma sollen in einer Hashtabelle
abgespeichert werden.
-  Dazu wird die Personalnummer als Schlüssel verwendet.
Die letzte Stelle der Personalnummer gibt das Geschlecht an:
0 = männlich und 1 = weiblich.
-  Eine Hashfunktion h = k mod m mit einer geraden Tabellengröße m würde
folgendes ergeben:
männliche Person:
h(x...xx0) ist gerade
weibliche Person:
h(x...xx1) ist ungerade
Ungleichmäßige Streuung bei ungleicher Geschlecherquote!
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-34
Tabellengröße als Primzahl bei Division-Rest-Methode (2)
!  Beispiel 2:
-  Die Personalnummer wird nun 6-stellig gewählt, wobei die hintersten
3 Stellen die Abteilung codieren, in der die Person arbeitet.
-  Eine Hashfunktion h = k mod m mit einer Tabellengröße m = 1000
würde folgendes ergeben:
h(xxxyyy) = yyy, wobei yyy die Abteilungsnummer ist.
Ungleichmäßige Streuung bei ungleicher Verteilung der Mitarbeiter auf
die einzelnen Abteilungen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-35
Einschub: Verteilung der Primzahlen
Primzahlfunktion
π(n) = Anzahl der Primzahlen ≤ n.
Primzahlsatz
π(n) ∼ n/ln(n)
Damit gilt für die Primzahlhäufigkeit:
π(n)/n ∼ 1/ln(n)
Tabelle zeigt die tatsächliche und die geschätzte Primzahlhäufigkeit
n
π(n)/n
1/ln(n)
104
0.123
0.11
105
0.096
0.087
106
0.078
0.072
107
0.066
0.062
108
0.058
0.054
109
0.051
0.048
10100
?
0.0043
10200
?
0.0021
Prof. Dr. O. Bittel, HTWG Konstanz
Im praktisch relevanten Bereich liegt
die Primzahlhäufigkeit bei 5 bis 10%.
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-36
Hashfunktion für Strings als Schlüssel
!  Interpretierte String s = z0...zn-2zn-1 (Folge von ASCII-Zeichen)
als eine Zahl im Stellenwertsystem 31:
h(z0...zn-2zn-1) = [z0*31n-1 + ... + zn-2*311 + zn-1*310] mod m;
!  31 ist eine Primzahl und führt zu einer guten Streuung.
!  Mithilfe des Horner-Schemas lässt sich der Ausdruck effizienter berechnen:
h(z0...zn-2zn-1) = [( ... (z0*31 + z1)*31 + ... + zn-2)*31 + zn-1] mod m;
!  Bei einer Implementierung der Hashfunktion kann ein evtl. auftretender
Überlauf ignoriert werden. Die Hashadresse kann dabei negativ werden, was
abgeprüft werden muss.
static int hash(String key){
int adr = 0;
for (int i = 0; i < key.length(); i++)
adr = 31*adr + key.charAt(i);
if (adr < 0)
adr = -adr;
return adr % m;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-37
Hashfunktion hashCode in Java (1)
!  In Java ist in der Klasse Object die Methode hashCode definiert, die jedes Objekt
auf einen ganzzahligen Wert abbildet.
class Object {
public int hashCode() {...}
// ...
}
!  Für alle Wrapper-Klassen wie Integer, Long, Short, etc. und für die Klasse String ist
hashCode geeignet überschrieben.
!  hashCode für String ist ähnlich implementiert wie die for-Schleife auf der
vorhergehenden Seite.
Es ist zu berücksichtigen, dass hashCode eine negative int-Zahl zurückliefern kann .
Wird der HashCode als Adresse für eine Hash-Tabelle benötigt, muss ein negativer
Wert abgefangen und modolo der Tabellengröße gerechnet werden:
String key = "Zimmer";
int adr = key.hashCode();
if (adr < 0)
adr = -adr;
adr = adr % m;
Prof. Dr. O. Bittel, HTWG Konstanz
liefert -1618018788
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-38
Hashfunktion hashCode in Java (2)
Hashfunktion für zusammengesetze Schlüssel
!  wird für einen zusammengesetzten Schlüsseltyp eine Hashfunktion benötigt,
dann überschreibt man am besten für den Schlüsseltyp die hashCode-Methode.
!  Dabei kann eine ähnliche Technik wir bei einem String-Schlüssel eingesetzt werden.
!  Beispiel:
public class Datum {
private int tag;
private int mon;
private int jahr;
// ...
@Override public int hashCode() {
int adr = 0;
adr = 31*adr + tag;
adr = 31*adr + mon;
adr = 31*adr + jahr;
return adr;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-39
Hashfunktion hashCode in Java (3)
Hashfunktion für zusammengesetze Schlüssel (Fortsetzung)
!  Im allgemeinen ist auch hier zu berücksichtigen, dass ein Überlauf auftreten kann und
hashCode eine negative int-Zahl zurückliefert.
!  Wird der HashCode als Adresse für eine Hash-Tabelle benötigt, muss ein negativer
Wert abgefangen und modolo der Tabellengröße gerechnet werden.
!  Beispiel:
Datum key = new Datum(24,12,2011);
int adr = key.hashCode();
if (adr < 0)
adr = -adr;
adr = adr % m;
ist in diesem Fall unnötig.
Bei Datum.hashCode kann es
keinen Überlauf geben.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-40
Adresskollision
!  Im allgemeinen ist eine Hashfunktion nicht injektiv.
D.h. unterschiedliche Schlüssel können die gleiche Hashadresse haben.
!  Beispiel: mit h(k) = k mod m und m = 7 gilt:
h(11) = h(25) = h(74) = 4.
!  Die Wahrscheinlichkeit einer Adresskollision ist sehr groß.
Sogar bei einer sehr gut streuenden Hashfunktion und einer im Vergleich zur
Schlüsselzahl großen Hashtabelle.
Das zeigt auch das sogenannte Geburtstagsparadoxon (s. nächste Folie)
!  Ansätze zur Kollisionsbehandung:
-  kollidierende Datensätze werden in linear verketteten Listen gehalten.
-  Offene Hashverfahren:
bei Kollision in der Tabelle nach freien Stellen sondieren
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-41
Einschub: Geburtstagsparadoxon
!  In einer Gruppe von 23 Personen ist es wahrscheinlich
(nämlich Wahrscheinlichkeit p ≈ 0.51),
dass 2 Personen am gleichen Tag Geburtstag haben.
!  Begründung:
Wahrscheinlichkeit p, dass alle n = 23 Personen
an unterschiedlichen Tagen Geburtstag haben:
364 363
343
p=€ *
**
≈ 0.49
365
365
365

 
2.Person
3.Person
23.Person
Damit ist die Wahrscheinlichkeit p, dass wenigstens zwei Personen
an einem gleichen Tag Geburtstag haben:
€
p = 1 − p ≈ 0.51
!  Konsequenz:
würde man die Daten von 23 Personen in eine Tabelle der Größe 365 mit einem
€Hashverfahren abbilden und als Hashfunktion
h(x) = Geburtstag der Person x auf {0, 1, 2, …, 364} umgerechnet
wählen, dann würde es wahrscheinlich eine Kollision geben.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-42
Teil I: Schlüsselbasiertes Suchen
!  Dictionaries
!  Elementare Suchverfahren
!  Hashverfahren
-  Idee
- 
- 
- 
- 
- 
! 
! 
! 
! 
Hashfunktion
Hashverfahren mit linear verketteten Listen
Offene Hashverfahren
Dynamische Hashverfahren
Hashverfahren in Java
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-43
Hashverfahren mit linear verketteten Listen (1)
Idee
Der Hashtabellen-Eintrag tab[i] zeigt auf eine verkette Liste, die alle Datensätze mit
der gleichen Hashadresse i enthalten.
Beispiel
Für m = 7 mit h(k) = k mod m ergibt sich nach dem Einfügen von
12, 53, 5, 15, 2, 19, 43
folgende Hashtabelle:
tab[0]
[1]
15
[2]
2
43
[3]
[4]
53
[5]
12
5
19
[6]
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-44
Hashverfahren mit linear verketteten Listen (2)
Algorithmen:
V search (K key) {
suche in tab[ h(key) ] nach Schlüssel key;
if (key gefunden)
return Daten des gefunden Datensatzes;
else
return null;
}
V insert (K key, V value) {
suche in tab[ h(key) ] nach Schlüssel key;
if (key gefunden)
// Schlüssel bereits vorhanden;
alte Daten durch value ersetzen;
return alte Daten;
else {
füge key am Ende oder
am Anfang der Liste an;
return null;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
V remove (K key) {
suche in tab[ h(key) ] nach Schlüssel key;
if (key gefunden) {
entferne Knoten k aus Liste;
return Daten von Knoten k;
}
else
return null;
}
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-45
Offene Hashverfahren (1)
Idee:
!  Alle Datensätze werden in einem Feld (d.h. keine verkettete Listen) untergebracht.
!  Falls beim Eintragen des Schlüssels k die Adresse h(k) bereits belegt ist, wird
gemäß einer Sondierungsfolge die erste freie Adresse gesucht und k dort abgespeichert.
tab[0]
[1]
[2]
[3]
h(18) = 18 mod 7 = 4
[4]
53
18
[5]
12
Sondierungsfolge
[6]
18
!  Beim Suchen von k wird ebenfalls die Sondierungsfolge durchlaufen.
!  Zu unterschiedlichen Zeitpunkten können gleiche Schlüssel auf unterschiedliche Adressen
abgebildet werden; daher auch die Bezeichnung Hashverfahren mit offener Adressierung.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-46
Offene Hashverfahren (2)
Allgemeine Sondierungsfolge
h(k) + s(j,k) mod m mit j = 0, 1, …, m-1
Beispiel (lineare Sondierungsfolge)
h(k) + j mod m
mit j = 0, 1, ..., m-1 und h(k) = k mod m
tab[0]
tab[0]
[1]
[1]
12, 53 und 4
eingefügt:
Prof. Dr. O. Bittel, HTWG Konstanz
[2]
10 und 18
eingefügt:
[3]
[4]
53
[5]
12
[6]
4
4
Sondierungsfolge für 4
18
[2]
[3]
10
[4]
53
[5]
12
[6]
4
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
18
SS 2017
1-47
Offene Hashverfahren (3)
Algorithmus zum Suchen:
V search (K key) {
if ( (adr = searchAdr(key) != -1)
return tab[adr].value;
else
return null;
}
int searchAdr (K key) {
j = 0;
do {
adr = (h(key) + s(j,key)) % m;
j++;
} while (tab[adr] != ″leer″ && tab[adr].key != key);
if (tab[adr] != ″leer″)
return adr;
}
else
return -1;
}
Wichtig:
Um Endlosschleifen bei stark gefüllten
Tabellen zu vermeiden, sind die Längen der
Sondierungsfolgen zu beschränken.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-48
Offene Hashverfahren (4)
Algorithmus zum Löschen:
V remove (K key) {
if ( (adr = searchAdr(key) != -1)
oldValue = tab[adr].value;
tab[adr] = ″gelöscht″ ;
return oldValue;
}
else
return null;
}
Wichtig:
Eine Hashadresse hat 3 Zustände:
•  Eintrag vorhanden
•  Eintrag gelöscht
•  Eintrag leer
Zu Beginn sind alle Einträge leer.
Grund:
Beim Löschen können Lücken
entstehen, die bei einer
späteren Suchoperation
übersprungen werden müssen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-49
Offene Hashverfahren (5)
Algorithmus zum Einfügen:
void insert (K key, V value) {
if ( (adr = searchAdr(key) != -1) {
oldValue = tab[adr].value;
tab[adr].value = value;
return oldValue;
}
// Neueintrag:
Es werden zuerst wieder
j = 0;
die Lücken gefüllt
do {
adr = (h(k) + s(j,k)) % m;
j++;
} while (tab[adr] != ″leer″ && tab[adr] != ″gelöscht″ );
tab[adr].key = key;
tab[adr].value = value;
return null;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-50
Lineares Sondieren
!  s(j,k) = j
!  Damit ergibt sich die Sondierungsfolge:
h(k)
h(k) + 1 mod m
h(k) + 2 mod m
…
!  Lineares Sondieren tendiert aufgrund von Sekundärkollisionen (zwei
Sondierungsfolgen überschneiden sich) zu Clusterbildung:
Große belegte Cluster haben eine stärkere Tendenz zu wachsen als kleinere.
Füge k ein
belegt
…
k
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-51
Quadratisches Sondieren
!  s(j,k) = j2
!  Damit ergibt sich die Sondierungsfolge:
h(k)
h(k) + 1 mod m
h(k) + 4 mod m
h(k) + 9 mod m
…
!  Quadratisches Sondieren streut wesentlich besser als lineares
Sondieren.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-52
Beachte beim quadratischen Sondieren
!  Es wird mit einer quadratischen Sondierungsfolge im
allgemeinen nicht jede Adresse erreicht.
!  Beispiel: Mit m = 8 und h(k) = 0 erreicht die Sondierungsfolge
nur 3 der 8 Einträge:
0
1
4
9
16
25
36
49
= 0 mod m,
= 1 mod m
= 4 mod m
= 1 mod m
= 0 mod m
= 1 mod m
= 4 mod m
= 1 mod m
!  Wenn m eine Primzahl ist, dann wird mit jeder Sondierungsfolge
wenigstens die Hälfte aller Einträge erreicht.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-53
Alternierend quadratisches Sondieren
!  Als Tabellengröße m wird eine Primzahl der Form 4i + 3 gewählt.
!  Die alternierend quadratische Sondierungsfolge wird definiert durch:
s(j,k) = ⎡j/2⎤2(-1)j
!  Die Sondierungsfolge lautet konkret:
h(k)
h(k) – 1 mod m
h(k) + 1 mod m
h(k) – 4 mod m
h(k) + 4 mod m
...
!  Aufpassen: mod ist mathematisch definiert und
unterscheidet sich für negative Zahlen vom Modolo-Operator % in Java.
!  Mit alternierend quadratischem Sondieren werden alle Einträge erreicht.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-54
Einschub: symmetrische und mathematische mod-Funktion
Symmetrische mod-Funktion:
(siehe % in Java und C/C++)
x mod m = x – (x/m)*m
(/ ist hier eine Ganzzahldivision)
Beispiele:
-11 mod 7 = -11 – (-11/7)*7 = -11 – (-1)*7 = -4
11 mod 7 = 11 – (11/7)*7 = 11 – 1*7 = 4
Mathematische mod-Funktion:
(siehe % in Python)
x mod m = x – ⎣x/m⎦*m
(/ ist hier eine Gleitkommadivision)
Beispiele:
-11 mod 7 = -11 – ⎣-11/7⎦*7 = -11 – (-2)*7 = 3
11 mod 7 = 11 – ⎣11/7⎦*7 = 11 – 1*7 = 4
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-55
Double Hashing
!  Für die Sondierungsfolge wird eine weitere Hashfunktion h' gewählt.
!  Die Sondierungsfolge wird definiert durch:
s(j,k) = j * h‘(k)
!  Damit ergibt sich folgende Sondierungsfolge:
h(k)
h(k) + 1*h‘(k) mod m
h(k) + 2*h‘(k) mod m
h(k) + 3*h‘(k) mod m
…
!  Die beiden Hashfunktionen sollten möglichst unabhängig sein.
Eine gute Wahl ist:
h(k) = k mod m
h‘(k) = (k+1) mod (m-2).
!  Außerdem ist es wichtig, dass die Tabellengröße m eine Primzahl ist.
Nur dann erreicht jede Sondierungsfolge alle Einträge.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-56
Analyse der Hashverfahren (1)
Analyse im schlechtesten Fall
!  Alle n Einträge haben die gleiche Hashadresse.
Daher muss jede Operation eine Liste mit bis zu n Einträgen ablaufen.
!  Dies ist in der Praxis so unwahrscheinlich, dass hier die Analyse im mittleren Fall
wesentlich wichtiger ist.
Analyse im mittleren Fall
!  Wichtige Maßzahlen:
C Anzahl der durchschnittlich betrachteten Einträge
bei erfolgreicher Suche.
C‘ Anzahl der durchschnittlich betrachteten Einträge bei
nicht erfolgreicher Suche.
!  Es zeigt sich, dass C und C‘ nur abhängig sind vom Belegungsfaktor α = n/m,
wobei n = Anzahl der Einträge und m = Tabellengröße.
!  Beachte, dass der Belegungsfaktor α bei Hashverfahren mit Verkettung größer 1
sein kann, jedoch bei bei offenen Hashverfahren kleiner gleich 1 sein muss.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-57
Analyse der Hashverfahren (2)
C und C‘ für verschiedene Hashverfahren
Verfahren
C
Hashverfahren
mit Verkettung
C’
α
Offenes Hashverfahren mit
linearem Sondieren
Offenes Hashverfahren mit
quadratischem Sondieren
double hashing mit
unabhängigen h u. h’
!  Die Angaben für C und C‘ setzen eine ideale Hashfunktion voraus.
D.h. die Schlüssel sind gleichmäßig über die Tabelle verstreut.
!  Die umfangreichen Herleitungen können in [Ottmann u. Widmayer 2002]
nachgelesen werden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-58
Analyse der Hashverfahren (3)
C und C‘ für konkrete Belegungsfaktoren:
α = 0.5
α = 2/3
α = 0.8
Verfahren
C
C’
C
C’
C
C’
Hashverfahren
mit Verkettung
1.25
0.5
1.33
0.66
1.4
0.8
Offenes Hashverfahren mit
linearem Sondieren
1.5
2.5
2
5
3
13
Offenes Hashverfahren mit
quadratischem Sondieren
1.44
2.19
1.77
3.43
2.21
5.81
double hashing mit
unabhängigen h u. h’
1.38
2
1.65
3
2.01
5
!  Bei offenen Hashverfahren wird in der Praxis üblicherweise ein Belegungsfaktor
von
α ≤ 2/3
angestrebt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-59
Teil I: Schlüsselbasiertes Suchen
!  Dictionaries
!  Elementare Suchverfahren
!  Hashverfahren
-  Idee
- 
- 
- 
- 
- 
! 
! 
! 
! 
Hashfunktion
Hashverfahren mit linear verketteten Listen
Offene Hashverfahren
Dynamische Hashverfahren
Hashverfahren in Java
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-60
Dynamische Hashverfahren (1)
Problem
!  Bei den bisher besprochenen Hashverfahren wird die Tabellengröße m einmalig
festgelegt und die Hashfunktion damit parametrisiert.
!  Wird ein bestimmter Füllungsgrad überschritten, dann kann das eingesetzte
Hashverfahren sehr langsam werden, so dass eine Umorganisation notwendig wird.
Vergrößern der Tabelle mit sofortigem Umkopieren:
!  Falls beim Einfügen ein bestimmter Füllungsgrad überschritten wird, werden
folgende Operationen durchgeführt:
-  Lege neue Tabelle tabʹ mit einer in etwa doppelten Größe mʹ an
(mʹ sollte Primzahl sein);
-  Füge alle Einträge aus alter Tabelle tab in die neue Tabelle tabʹ ein;
-  Das alte Feld tab wird gelöscht und durch das neue ersetzt;
!  Problem:
Die insert-Operation, die zu einer Umorganisation der Hashtabelle führt, wird
singulär sehr aufwendig. Wenn danach jedoch noch viele weitere insertOperationen durchgeführt werden, wird der Aufwand jedoch amortisiert.
Problematisch kann der singulär erhöhte Aufwand bei einem interaktiven System
sein.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-61
Dynamische Hashverfahren (2)
Vergrößern der Tabelle mit verzögertem Umkopieren (lazy copying)
!  nur für Hashverfahren mit Verkettung
!  Falls beim Einfügen ein bestimmter Füllungsgrad überschritten wird:
-  Lege neue Tabelle tabʹ mit einer in etwa doppelten Größe mʹ an
(mʹ sollte Primzahl sein).
-  Bei jedem Zugriff auf die Tabelle (search, insert bzw. remove) wird zusätzlich der
nicht-leere Tabelleneintrag (komplette verkettete Liste) mit kleinstem Index min
in die neue Tabelle übertragen.
-  Entscheide bei jedem Zugriff, ob Daten in alter oder neuer Tabelle stehen:
Falls h(k) = k mod m ≤ min, dann greife auf neue Tabelle tabʹ zu,
sonst auf alte Tabelle tab.
-  Beachte, dass bei alter Tabelle tab mit h(k) = k mod m
und bei neuer Tabelle tabʹ mit hʹ(k) = k mod mʹ zugegriffen wird.
-  Falls alte Tabelle tab nicht mehr gebraucht wird
(d.h. min = Größe der Tabelle tab), wird tab gelöscht und durch tabʹ ersetzt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-62
Dynamische Hashverfahren (3)
tab[0]
Beispiel
[1]
16
41
[2]
12
7
[3]
23
48
21
26
8
13
[4]
insert(9):
•  Lege neue Tabelle tabʹ der Größe 11 an, da Füllungsgrad von tab überschritten wird;
•  Übertrage kleinsten Tabelleneintrag tab[min] mit min = 1 nach tabʹ;
•  Füge nun 9 in alte Tabelle ein (da h(9) = 9 mod 5 = 4 > min).
tab‘[0]
[1]
min
tab[0]
[2]
[1]
[3]
[2]
12
7
[3]
23
48
[4]
9
8
13
[4]
26
[5]
16
[6]
[7]
[8]
41
[9]
[10]
Prof. Dr. O. Bittel, HTWG Konstanz
21
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-63
Dynamische Hashverfahren (4)
Beispiel (Fortsetzung):
insert(32):
•  Übertrage kleinsten Tabelleneintrag tab[min] mit min = 2 nach tabʹ;
•  Füge nun 32 in neue Tabelle ein (h(32) = 32 mod 5 = 2 ≤ min).
Da hʹ(32) = 32 mod 11 = 10 ist, wird 32 beim Index 10 eingetragen.
tab‘[0]
[1]
min
12
tab[0]
[2]
[1]
[3]
[2]
[4]
26
[5]
16
[3]
23
[4]
9
48
8
13
[6]
[7]
7
[8]
41
[9]
[10]
Prof. Dr. O. Bittel, HTWG Konstanz
21
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
32
SS 2017
1-64
Hashverfahren in Java
Iterable<K>
Interface
Klasse
Collection<K>
Set<K>
Map<K,V>
HashSet<K>
HashMap<K,V>
LinkedHashSet<K>
LinkedHashMap<K,V>
extends
implements
!  Bei LinkedHashSet und LinkedHashMap wird zusätzlich mit einer doppelt verketteten
Liste über die Reihenfolge des Einfügens Buch geführt.
!  Bei LinkedHashMap kann statt der Einfüge-Reihenfolge auch die Zugriffs-Reihenfolge
eingestellt werden: least-recently to most-recently accessed
!  Außer HashMap gibt es noch die Varianten WeakHashMap und IdentityHashMap.
!  Wichtig: hashCode und equals muss für den Schlüsseltyp geeignet überschrieben
werden. Falls o1.equals(o2), dann muss auch o1.hashCode() == o2.hashCode() sein.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-65
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
-  Begriffe, Eigenschaften und Traversierung (Wiederholung PROG 2)
-  Binäre Suchbäume (Wiederholung PROG 2)
-  Traversierung von Bäumen mit Elternzeiger
!  Ausgeglichene Bäume
!  Digitale Suchbäume
!  kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-66
Bäume: Begriffe und Eigenschaften (1)
Wurzel
Elternknoten zu s und t
Kante
Rechtes Kind von k
k
Linkes Kind von k
s
t
Linker
Teilbaum
von k
!  Jeder Knoten hat maximal ein Elternknoten.
Rechter
Teilbaum
von k
Blatt
Knoten
!  Die Wurzel ist der einzige Knoten, der kein Elternknoten hat.
!  Jeder Knoten kann beliebig viele Kinder haben.
!  Keine Zyklen
!  Blätter haben keine Kinder.
!  Spezialfall Binärbaum: Jeder Knoten hat maximal 2 Kinder (linkes bzw. rechtes Kind)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-67
Bäume: Begriffe und Eigenschaften (2)
Höhe eines Baums
Maximale Anzahl von Kanten von seiner Wurzel zu einem Blatt.
Beispiel
10 3
5
3 0
2
7
6
Höhe für jeden Teilbaum
12 1
1
11 0
15
0
0
Beachte
•  Ein Baum, der nur aus einem Knoten besteht, besitzt die Höhe 0.
•  Aus technischen Gründen wird die Höhe eines leeren Baums
(d.h. Anzahl Knoten = 0) als -1 definiert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-68
Bäume: Begriffe und Eigenschaften (3)
Vollständiger Binärbaum
Ein vollständiger Binärbaum ist ein Binärbaum, bei der jede Ebene (bis auf die letzte)
vollständig gefüllt und die letzte Ebene von links nach rechts gefüllt ist.
Beispiele
Eigenschaften
•  Ein vollständiger Binärbaum mit n Knoten hat die Höhe h = ⎣log2n⎦.
•  Vollständige Binärbäume sind (bei einer gegebenen Knotenzahl) Binärbäume mit einer
minimalen Höhe.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-69
Bäume: Begriffe und Eigenschaften (4)
Implementierung von Binärbäumen
mit verketteten Knoten
root
!  Jeder Knoten hat jeweils eine Referenz
für das linke und das rechte Kind.
A
B
B
D
C
E
D
H
E
F
H
I
I
Einfachheitshalber sind im Baum
nur die Schlüssel dargestellt.
Prof. Dr. O. Bittel, HTWG Konstanz
C
F
G
G
A
class Node<K,V> {
K key;
V value;
Node<K,V> left;
Node<K,V> right;
}
// linkes Kind
// rechtes Kind
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-70
Traversierung von Binärbäumen
Ziel
!  Das Besuchen aller Knoten in einer bestimmten Reihenfolge
ist eine oft benötigte Operation.
Durchlaufreihenfolge
! 
! 
! 
! 
PreOrder: besuche Wurzel, besuche linken Teilbaum; besuche rechten Teilbaum;
PostOrder: besuche linken Teilbaum; besuche rechten Teilbaum; besuche Wurzel;
InOrder:
besuche linken Teilbaum; besuche Wurzel; besuche rechten Teilbaum;
LevelOrder: besuche Knoten ebenenweise
Bemerkungen
!  Die Präfixe Pre, Post bzw. In bedeuten vorher, nachher und dazwischen.
Gemeint ist damit der Zeitpunkt, an dem die Wurzel besucht wird.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-71
PreOrder-Durchlauf
static void preOrder(Node<K,V> p)
{
if (p != null){
bearbeite(p);
preOrder(p.left);
preOrder(p.right);
}
}
static class Node<K,V> {
K key;
V value;
Node<K,V> left;
Node<K,V> right;
}
private Node<K,V> root;
//...
static void main(...) {
preOrder(root);
}
Beispiel
*
-
a
c
Durchlaufreihenfolge: * - a b c
b
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-72
PostOrder-Durchlauf
static void postOrder(Node<K, V> p) {
if (p != null){
postOrder(p.left);
postOrder(p.right);
bearbeite(p);
}
}
Beispiel
*
-
c
Durchlaufreihenfolge: a b – c *
a
b
Prof. Dr. O. Bittel, HTWG Konstanz
(Entspricht der sog. Postfix-Notation für
arithmetische Ausdrücke)
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-73
InOrder-Durchlauf für Binärbäume
static void inOrder(Node<K, V> p) {
if (p != null){
inOrder(p.left);
bearbeite(p);
inOrder(p.right);
}
}
Beispiel
*
-
a
c
Durchlaufreihenfolge: a - b * c
b
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-74
LevelOrder-Durchlauf
static void levelOrder(Node<K,V> p) {
Queue<Node<K,V>> queue = new ArrayDeque<Node<K,V>>();
queue.add(p);
while (!queue.isEmpty()) {
Node<K,V> q = queue.remove();
if (q != null) {
bearbeite(q);
queue.add(q.left);
queue.addd(q.right);
}
}
Die Knoten werden ebenenweise
in einer Schlange gespeichert
und in einer while-Schleife
abgearbeitet.
}
1
2
4
3
5
Prof. Dr. O. Bittel, HTWG Konstanz
6
Durchlaufreihenfolge: 1, 2, 3, 4, 5, 6
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-75
Definition binärer Suchbäumen
!  Ein binärer Suchbaum ist ein Binärbaum, bei dem für alle Knoten k folgende
Eigenschaften gelten:
-  Alle Schlüssel im linken Teilbaum sind kleiner als k
-  Alle Schlüssel im rechten Teilbaum sind größer als k
!  Beachte, dass die Zahlen hier eindeutig sein müssen. Es ist aber auch
möglich, dass gleiche Zahlen mehrfach vorkommen dürfen, was kleine
Änderungen in den Algorithmen erfordert.
Beispiele:
7
6
1
Degenerierter
Suchbaum
2
1
8
2
4
3
Prof. Dr. O. Bittel, HTWG Konstanz
8
3
4
6
3
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
4
7
SS 2017
1-76
Klasse BinarySearchTree
public class BinarySearchTree<K, V> {
static private class Node<K, V> {
private K key;
private V value;
private Node<K, V> left;
private Node<K, V> right;
private Node(K k, V v) {
key = k;
value = v;
left = null;
right = null;
}
}
private Node<K, V> root = null;
// ...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-77
Suchen in binären Suchbäumen
public V search(K key) {
return searchR(key, root);
}
Beispiel
private V searchR(K key, Node<K,V> p) {
if (p == null)
return null;
else if (key.compareTo(p.key) < 0)
return searchR(key, p.left);
else if (key.compareTo(p.key) > 0)
return searchR(key, p.right);
else
return p.value;
}
Prof. Dr. O. Bittel, HTWG Konstanz
7
root
2
1
8
4
3
6
searchR(3, root)
liefert true
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-78
Einfügen in binären Suchbäumen (1)
Idee
!  Um eine Zahl x einzufügen, wird zunächst nach x gesucht.
!  Falls x nicht bereits im Baum vorkommt,
endet die Suche erfolglos bei einer null-Referenz.
!  An dieser Stelle wird dann ein neuen Knoten mit Eintrag x eingefügt.
Beispiel 1: füge 6 ein
Beispiel 2: füge 8 ein
7
2
1
7
9
4
3
Suche von 6 endet
bei null
Prof. Dr. O. Bittel, HTWG Konstanz
2
1
7
9
4
3
2
1
6
Ersetzte null durch neuen
Knoten mit Eintrag 6
7
9
4
3
2
1
6
Suche von 8 endet
bei null
9
4
3
8
6
Ersetzte null durch
Knoten mit Eintrag 8
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-79
Einfügen in binären Suchbäumen (2)
V oldValue;
// Rückgabeparameter
public V insert(K key, V value) {
root = insertR(key, value, root);
return oldValue;
}
private Node<K,V> insertR(K key, V value, Node<K,V> p) {
if (p == null) {
p = new Node(key, value);
oldValue = null;
}
else if (key.compareTo(p.key) < 0)
p.left = insertR(key, value, p.left);
else if (key.compareTo(p.key) > 0)
p.right = insertR(key, value, p.right);
else { // Schlüssel bereits vorhanden:
oldValue = p.value;
p.value = value;
}
return p;
}
Prof. Dr. O. Bittel, HTWG Konstanz
7
root
2
1
root
9
4
3
7
2
1
9
4
3
6
root = insert(6, root);
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-80
Löschen in binären Suchbäumen (1)
Idee
!  Um eine Zahl x zu löschen, wird zunächst nach x gesucht.
Es sind dann 4 Fälle zu unterscheiden:
!  Fall „Nicht vorhanden“:
x kommt nicht vor. Dann ist nichts zu tun.
!  Fall „Keine Kinder“:
x kommt in einem Blatt vor (keine Kinder):
dann kann der Knoten einfach entfernt werden.
!  Fall „Ein Kind“:
Der Knoten, der x enthält, hat genau ein Kind:
s. nächste Folie
!  Fall „Zwei Kinder“:
Der Knoten, der x enthält, hat zwei Kinder:
s. übernächste Folie
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-81
Löschen in binären Suchbäumen (2)
Fall: der zu löschende Knoten k hat ein Kind
!  Überbrücke den Knoten k, indem der Elternknoten von k auf das Kind von k
verzeigert wird (Bypass).
Beispiel: lösche Knoten k mit Inhalt 4
7
2
1
7
9
4
k
3
Suche 4
Prof. Dr. O. Bittel, HTWG Konstanz
2
1
7
9
4
k
3
Knoten 4 wird
überbrückt (Bypass)
2
9
1
3
Knoten 4 wird
gelöscht
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-82
Löschen in binären Suchbäumen (3)
Fall: der zu löschende Knoten k hat zwei Kinder
!  Ersetze den Knoten k durch den kleinsten Knoten kmin im rechten Teilbaum von k.
!  Lösche dann kmin.
!  Da der Knoten kmin kein linkes Kind haben kann, kann das Löschen von kmin
wie im Fall „Ein Kind“ bzw. „Keine Kinder“ behandelt werden.
Beispiel: lösche Knoten k mit Inhalt 2
k 2
1
k 2
9
1
5
3
7
7
7
6
4
4
Suche kleinsten Knoten kmin
in rechten Teilbaum von k
Prof. Dr. O. Bittel, HTWG Konstanz
k 3
1
5
kmin 3
6
Suche k mit Inhalt 2
9
7
9
5
kmin 3
3
1
9
5
6
4
Knoten k wird
durch kmin ersetzt
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
6
4
kmin wird gelöscht
SS 2017
1-83
Löschen in binären Suchbäumen (4)
public V remove(K key) {
root = removeR(k, root);
return OldValue;
}
private Node<K,V> removeR(K key, Node<K,V> p) {
if (p == null) { oldValue = null; }
else if(key.compareTo(p.key) < 0)
p.left = removeR(key, p.left);
else if (key.compareTo(p.key) > 0)
p.right = removeR(key, p.right);
else {
if (p.left == null || p.right == null) {
// p hat ein oder kein Kind:
oldValue = p.value;
p = (p.left != null) ? p.left : p.right;
} else {
// p hat zwei Kinder:
Entry<K,V> min = new Entry<K,V>();
p.right = getRemMinR(p.right, min);
oldValue = p.value;
p.key = min.key;
p.value = min.value;
}
}
return p;
}
Prof. Dr. O. Bittel, HTWG Konstanz
private Node<K,V> getRemMinR(Node<K,V> p, Entry<K,V> min) {
assert p != null;
if (p.left == null) {
min.key = p.key;
min.value = p.value;
p = p.right;
}
else
p.left = getRemMinR(p.left, min);
return p;
}
private static class Entry<K, V> {
private K key;
private V value;
}
!  getRemMinR löscht im Baum p den Knoten mit
kleinstem Schlüssel und liefert Schlüssel und
Daten des gelöschten Knotens über min zurück
!  Entry ist Hilfsdatentyp für Rückgabe-Parameter
min von getRemMinR
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-84
Analyse
Worst-Case
!  Im schlechtesten Fall kann ein binärer Suchbaum mit n Knoten zu einem Baum der
Höhe n-1 entarten.
!  Damit: Tmax(n) = O(n)
Average-Case
!  In [Ottmann und Widmayer 2002] werden zwei Ergebnisse hergeleitet, die sich darin
unterscheiden, welche Verteilung der Bäume angenommen wird.
!  Bäume mit n Knoten entstehen durch eine Folge von Einfüge-Operationen von n
unterschiedlichen Elementen. Es wird angenommen, dass jede der n! möglichen
Anordnungen der Elemente gleich wahrscheinlich ist.
Damit: Tmit(n) = O(log2n)
!  Es wird angenommen, dass alle strukturell verschiedenen binären Suchbäume mit n
Knoten gleichwahrscheinlich sind.
Damit: Tmit(n) = O(
Prof. Dr. O. Bittel, HTWG Konstanz
)
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-85
Traversierung von Binärbäumen
Problem
!  In binären Suchbäumen gibt es für einen Knoten im allgemeinen keinen effizienten
Zugriff auf seinen InOrder-Vorgänger bzw. -Nachfolger.
!  Daher ist mit den bisher besprochenen Suchbäumen eine effiziente Vorwärts- bzw.
Rückwärtstraversierung mit Iteratoren nicht machbar.
10
5
!  Wie erreicht man den
Nachfolger von 8?
12
!  Wie erreicht man den
Vorgänger von 6?
3
1
Prof. Dr. O. Bittel, HTWG Konstanz
7
6
15
8
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-86
Bäume mit Elternzeigern
!  Erweitere jeden Knoten um einen Zeiger auf den Elternknoten.
10
12
5
3
1
Prof. Dr. O. Bittel, HTWG Konstanz
15
7
6
8
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-87
Klasse BinarySearchTree mit Elternzeiger (1)
!  Erweitere Klasse Node
um Elternzeiger parent:
public class BinarySearchTree<K, V> {
static private class Node<K, V> {
private Node<K, V> parent; // Elternzeiger
private K key;
private V value;
private Node<K, V> left;
private Node<K, V> right;
private Node(K k, V v) {
key = k;
value = v;
left = null;
right = null;
parent = null;
}
}
private Node<K, V> root = null;
// ...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-88
Klasse BinarySearchTree mit Elternzeiger (2)
!  Überall, wo die Struktur des Baums geändert wird, wird zusätzlich der
parent-Zeiger neu gesetzt:
expr1.left = expr2;
if (expr1.left != null)
expr1.left.parent = expr1;
expr1.left = expr2;
10
expr1
10
...
5
expr2
...
expr1
...
expr2
5
...
...
...
...
expr1 und expr2 sind beliebige Ausdrücke vom Typ Node.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-89
Klasse BinarySearchTree mit Elternzeiger (3)
!  Analog:
expr1.right = expr2;
expr1.right = expr2;
if (expr1.right != null)
expr1.right.parent = expr1;
!  Beachte, dass der parent-Zeiger des root-Knotens den Wert null bekommt:
root = expr;
Prof. Dr. O. Bittel, HTWG Konstanz
root = expr;
if (root != null)
root.parent = null;
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-90
Beispiel: insert-Methode mit Elternzeiger
public V insert(K key, V value) {
root = insertR(key, value, root);
if (root != null)
root.parent = null;
return oldValue;
}
Prof. Dr. O. Bittel, HTWG Konstanz
private Node<K,V> insertR(K key, V value, Node<K,V> p) {
if (p == null) {
p = new Node(key, value);
oldValue = null;
}
else if (key.compareTo(p.key) < 0) {
p.left = insertR(key, value, p.left);
if (p.left != null)
p.left.parent = p;
} else if (key.compareTo(p.key) > 0) {
p.right = insertR(key, value, p.right);
if (p.right != null)
p.right.parent = p;
} else { // Schlüssel bereits vorhanden:
oldValue = p.value;
p.value = value;
}
return p;
}
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-91
Traversierung von Bäumen
!  Hier: nur Traversierung zum InOrder-Nachfolger.
Traversierung zum InOrder-Vorgänger geht analog.
!  Sei p der aktuelle Knoten. Gesucht ist der Nachfolger von p.
Wir unterscheiden zwei Fälle:
if (p.right != null)
p = leftMostDescendant(p.right);
else
p = parentOfLeftMostAncestor(p);
parentOfLeftMostAncestor(p)
p
...
...
...
...
p
leftMostDescendant(p.right)
...
Prof. Dr. O. Bittel, HTWG Konstanz
...
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-92
leftMostDescendant
10
InOrder-Nachfolger von p = 5:
leftMostDescendant(p.right) = 6
12
5
3
1
15
7
6
Prof. Dr. O. Bittel, HTWG Konstanz
8
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-93
parentOfLeftMostAncestor
10
InOrder-Nachfolger von p = 8:
parentOfLeftMostAncestor(p) = 10
12
5
3
1
15
7
6
Prof. Dr. O. Bittel, HTWG Konstanz
8
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-94
Implementierung von leftMostDescendant
private Node<K,V> leftMostDescendant(Node<K,V> p) {
assert p != null;
while (p.left != null)
p = p.left;
return p;
}
p
...
leftMostDescendant(p.right)
...
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-95
Implementierung von parentOfLeftMostAncestor
private Node<K,V> parentOfLeftMostAncestor(Node<K,V> p) {
assert p != null;
while (p.parent != null && p.parent.right == p)
p = p.parent;
return p.parent;
// kann auch null sein
}
parentOfLeftMostAncestor(p)
...
...
p
...
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-96
Traversierungsschleife für InOrder-Nachfolger
// Erster Knoten:
Node<K,V> p = leftMostDescendant(root);
System.out.print(p.key + ", ");
10
while( p != null) {
if (p.right != null)
p= leftMostDescendant(p.right);
else
p = ParentOfLeftMostAncestor(p);
System.out.print(p.key + ", ");
}
12
5
3
1
15
7
6
8
!  Traversersierung-Schleife ergibt:
1, 3, 5, 6, 7, 8, 10, 12, 15
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-97
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
-  AVL-Bäume
-  B-Bäume
-  2-3-4-Bäume und Rot-Schwarz-Bäume
!  Digitale Suchbäume
!  kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-98
Definition von AVL-Bäumen
Definition
!  Ein binärer Suchbaum heißt AVL-Baum oder (höhen)balanzierter Baum, falls sich für
jeden Knoten k die Höhen der beiden Teilbäume um höchstens 1 unterscheiden.
!  Die Abkürzung AVL geht zurück auf Adelson-Velskij und Landis.
Beispiel für AVL-Baum:
5
10 3
10 3
-1
-2
2
5
12 1
+1
0
3 0
7
0
-1
6
Beispiel für Nicht-AVL-Baum
1
0
0
11 0
0
2
12 0
0
+2
15
0
0
7
Höhe des Teilbaums
Höhenunterschied
= Höhe rechter Teilbaum
− Höhe linker Teilbaum
1
+1
8
0
0
(Beachte: Höhe eines leeren
Teilbaums ist -1.)
Eigenschaft
!  Ein AVL-Baum hat eine maximale Höhe von etwa 1.5 log2n. [Ottmann u. Widmayer].
!  Damit lassen sich alle Dictionary-Operationen in O(log n) realisieren.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-99
Rotationen (1)
Zentrale Eigenschaft von AVL-Bäumen:
Ein unbalanzierter Baum, der einen Höhenunterschied von -2 (linkslastiger Baum)
oder +2 (rechtslastiger Baum) hat und dessen Teilbäume ausbalanziert sind, lassen
sich durch eine einfache Rotationsoperation ausbalanzieren.
Fall A:
Baum ist linkslastig, d.h. Höhenunterschied = -2
Unterfall A1: linker Teilbaum hat Höhenunterschied -1 oder 0:
q
p
-2
q
h-1
h+1
-1 oder 0
A
B
Prof. Dr. O. Bittel, HTWG Konstanz
h
p
h od.
h+1
A
C
h od.
h-1
h
1 od. 0
Rotate
Right
Teilbäume A, B, und C
sind ausbalanziert und
können auch leer sein.
h od.
h-1
B
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
h-1
C
SS 2017
1-100
Rotationen (2)
Fall A:
Baum ist linkslastig, d.h. Höhenunterschied = -2
Unterfall A2: linker Teilbaum hat Höhenunterschied +1:
2.
p
1.
-2
q
h+1
h-1
+1
Rotate Left
Right
r
0
D
h-1
r
h
h-1 od.
h-2
A
B
q
h
p
h
h-2 od.
h-1
C
Prof. Dr. O. Bittel, HTWG Konstanz
A
B
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
C
D
SS 2017
1-101
Rotationen (3)
Fall B:
Baum ist rechtslastig, d.h. Höhenunterschied = +2
Unterfall B1: rechter Teilbaum hat Höhenunterschied 0 oder +1:
q
p
+2
h-1
q
h+1
p
0 oder +1
A
B
h od.
h+1
0 oder +1
h od.
h-1
Prof. Dr. O. Bittel, HTWG Konstanz
0 od. -1
Rotate Left
h-1
h
C
A
h
C
h od.
h-1
B
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-102
Rotationen (4)
Fall B:
Baum ist rechtslastig, d.h. Höhenunterschied = +2
Unterfall B2: rechter Teilbaum hat Höhenunterschied -1:
2.
p
+2
h-1
h+1
Rotate
Right Left
1.
q
r
0
-1
A
r
h
h-2 od.
h-1
B
h-1
h-1 od.
h-2
C
Prof. Dr. O. Bittel, HTWG Konstanz
p
h
q
h
D
A
B
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
C
D
SS 2017
1-103
Einfügen und Löschen in AVL-Bäumen
Idee für Algorithmus:
1)  Füge ein bzw. lösche wie bei binären Suchbäumen.
2)  Gehe dann von der Einfügestelle bzw. Löschstelle bis zur Wurzel
und balanziere falls notwendig mit einer Rotatationsoperation lokal
aus.
Bemerkung:
!  Da der Baum vor dem Einfügen bzw. Löschen ausbalanziert war, haben
alle Teibäume einen Höhenunterschied von -1, 0 oder +1.
Damit können nach dem Schritt 1) nur die Fälle A1, A2, B1 oder B2
auftreten.
!  Schritt 2) läßt sich geschickt beim Aufstieg aus der Rekursion
durchführen.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-104
Einfügen in AVL-Bäumen (1)
Beispiel:
2
2
Füge 5 ein
3
1
3
1
Gehe vom Knoten 5 in Richtung
Wurzel bis zum ersten
unbalanzierten Teilbaum.
4
4
5
2
1
2
Rotate Left
(Fall B1)
3
1
4
+2
4
3
5
+1
5
0
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-105
Einfügen in AVL-Bäumen (2)
Beispiel:
4
4
2
2
6
Gehe bis zum ersten
unbalanzierten
Teilbaum.
6
Füge 13 ein
3
1
5
7
3
1
14
5
14
7
15
15
13
4
4
2
6
2.
+2
1
3
1.
5
2
1
14
7
Rotate
Right Left
(Fall B2)
15
7
3
6
5
14
13
15
13
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-106
Algorithmen (1)
!  Node wie bei binären Suchbäumen.
Zusätzlich wird für jeden Knoten im Baum noch die
Höhe des entsprechenden Teilbaums abgespeichert.
static private class Node<K, V> {
int height;
K key;
V value;
Node<K, V> left;
Node<K, V> right;
private Node(K k, V v) {
height = 0;
key = k;
value = v;
left = null;
right = null;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
private int getHeight(Node<K,V> p) {
if (p == null)
return -1;
else
return p.height;
}
private int getBalance(Node<K,V> p) {
if (p == null)
return 0;
else
return getHeight(p.right) - getHeight(p.left);
}
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-107
Algorithmen (2)
!  Erweitere die rekursive insertR-Operation für binäre Suchbäume um eine
Balanzieroperation, die beim rekursiven Aufstieg durchgeführt wird.
private Node<K,V> insertR(K key, V value, Node<K,V> p) {
if (p == null) {
p = new Node(key, value);
oldValue = null;
}
else if (key.compareTo(p.key) < 0)
p.left = insertR(key, value, p.left);
else if (key.compareTo(p.key) > 0)
p.right = insertR(key, value, p.right);
else { // Schlüssel bereits vorhanden:
oldValue = p.value;
p.value = value;
}
p = balance(p);
return p;
}
!  Die removeR- und getRemMinR-Operation werden analog erweitert.
!  Die searchR-Operation bleibt unverändert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-108
Algorithmen (3)
private Node<K,V> balance(Node<K,V> p) {
if (p == null)
return null;
p.height = Math.max(getHeight(p.left), getHeight(p.right)) + 1;
if (getBalance(p) == -2) {
if (getBalance(p.left) <= 0)
Fall A1
p = rotateRight(p);
else
p = rotateLeftRight(p);
Fall A2
}
else if (getBalance(p) == +2) {
if (getBalance(p.right) >= 0)
p = rotateLeft(p);
Fall B1
else
p = rotateRightLeft(p);
Fall B2
}
return p;
}
Prof. Dr. O. Bittel, HTWG Konstanz
Höhe aktualisieren.
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-109
Algorithmen (3)
private Node<K,V> rotateRight(Node<K,V> p) {
assert p.left != null;
Node<K, V> q = p.left;
p.left = q.right;
q.right = p;
p.height = Math.max(getHeight(p.left), getHeight(p.right)) + 1;
q.height = Math.max(getHeight(q.left), getHeight(q.right)) + 1;
return q;
}
Rotate
Right
p
q
q
p
C
A
Prof. Dr. O. Bittel, HTWG Konstanz
B
A
B
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
C
SS 2017
1-110
Algorithmen (4)
private Node<K,V> rotateLeft(Node<K,V> p) {
// analog zu rotateRight
}
private Node<K,V> rotateLeftRight(Node<K,V> p) {
assert p.left != null;
p.left = rotateLeft(p.left);
return rotateRight(p);
}
private Node<K,V> rotateRightLeft(Node<K,V> p) {
assert p.right != null;
p.right = rotateRight(p.right);
return rotateLeft(p);
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-111
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
-  AVL-Bäume
-  B-Bäume
-  2-3-4-Bäume und Rot-Schwarz-Bäume
!  Digitale Suchbäume
!  kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-112
Definition von B-Bäumen der Ordnung m
1.  Balanzierung: Jedes Blatt hat die gleiche Tiefe.
2.  Anzahl Schlüssel:
-  Jeder Knoten hat maximal m-1 Schlüssel.
-  Jeder Knoten außer der Wurzel hat minimal ⎡m/2⎤ -1 Schlüssel.
3.  Anzahl Kinder:
Jeder Knoten (außer den Blättern) mit i vielen Schlüsseln hat genau i+1 Kinder.
(Beachte, dass ein B-Baum aus genau einem Knoten – die Wurzel – bestehen darf)
4.  Schlüsselordnung:
Für jeden Knoten K mit i-1 Schlüsseln k0, k1, …, ki-2 und i Kindern
mit ihren Teilbäumen B0, B1, …, Bi-1 gilt folgende Beziehung:
Schlüssel in B0 < k0 < Schlüssel in B1 < k1 < Schlüssel in B2 < …
Schlüssel in Bi-2 < ki-2 < Schlüssel in Bi-1 .
k0
k1
…
ki-2
Knoten K mit seinen i-1 Schlüsseln
i Kinder mit ihren Teilbäumen
B0
Prof. Dr. O. Bittel, HTWG Konstanz
B1
B2
…
Bi-2
Bi-1
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-113
Beispiele für B-Bäume der Ordnung m = 4
B-Baum der Höhe 0
20
B-Baum der Höhe 1
20
60
10
60
90
B-Baum der Höhe 2
20
10
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
30
21 24
33
60
35
50
38 43 45
70
52 55
90
65 67 69 80 85
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
96
SS 2017
1-114
Suchen in B-Bäumen
V search(K key, Node<K,V> p) {
if (p == 0)
return null;
// nicht gefunden;
suche key in Schlüsselmenge von Knoten p (beispielsweise mit binärer Suche);
if (key gefunden)
return Daten;
}
p = Teilbaum, in dem key liegen könnte;
return search(key, p);
Rekursiver Aufruf
Beispiel: Suchen nach k = 43
20
10
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
30
21 24
33
60
35
50
38 43 45
70
52 55
90
65 67 69 80 85
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
96
SS 2017
1-115
Einfügen in B-Bäumen
V insert(K key, V value, Node<K,V> p) {
suche Schlüssel key im Baum p;
if (key gefunden) {
speichere Daten in oldValue;
ersetze Daten durch value;
return oldValue;
}
füge key,value an der Blattstelle ein,
wo die Suche beendet wurde;
if (kein Schlüsselüberlauf)
return null;
!  Schlüsselüberlauf, falls Anzahl der
Schlüssel gleich m.
!  insert kann wie bei AVL-Bäumen
rekursiv realisiert werden.
Die Behandlung der Schlüsselüberläufe geschieht dann beim
rekursiven Aufstieg.
!  Muss die Wurzel gesplittet werden,
dann entsteht eine neue Wurzel
(siehe Beispiel)
for (alle Knoten k vom aktuellen Knoten bis zur Wurzel) {
if (Schlüsselüberlauf bei k)
führe Splittoperation für k durch;
else
return null;
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-116
Splitoperation
...
...
Elternknoten
k0 k1 … Kmit-1 kmit Kmit+1 … km-1
Splitoperation
...
k0 k1 … Kmit-1
Prof. Dr. O. Bittel, HTWG Konstanz
Aktueller Knoten mit
Schlüsselüberlauf
(Knoten enthält genau m Schlüssel)
Teile Schlüsselmenge an der
Stelle mit = ⎣m/2⎦
kmit
...
Kmit+1 … km-1
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-117
Beispiel 1: Einfügen von 82
20
10
11 12 15
5 7
30
21 24
35
33
10
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
30
21 24
33
50
38 43 45
20
1) Suchen von 82
60
70
52 55
65 67 69 80 85
50
38 43 45
96
2) 82 einfügen;
keine Splittoperation
notwendig, da genügend Platz
60
35
90
70
52 55
90
65 67 69 80 82 85 96
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-118
Beispiel 2: Einfügen von 39
20
10
11 12 15
5 7
30
35
33
21 24
1) Suchen von 39
60
50
38 43 45
70
52 55
65 67 69
90
80 82 85
96
2) 39 einfügen
20
10
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
30
21 24
33
60
35
3) Splittoperation anwenden,
da nicht genügend Platz
50
38 39 44 45 52 55
70
90
65 67 69 80 82 85 96
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-119
Beispiel 2: Einfügen von 39 (Fortsetzung)
20
30
10
5 7
11 12 15
21 24
33
35
38 39
20
30
10
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
21 24
33
4) weitere Splittoperation
anwenden
60
44
35
38 39
44
50
45
70
52 55
65 67 69 80 82 85 96
5) Fertig!
60
50
45
90
70
52 55
90
65 67 69 80 82 85 96
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-120
Beispiel 3: Erzeugen eines neuen Knotens bei einer Einfüge-Operation
B-Baum mit genau
einem Knoten
mit 3 Schlüsseln
20
10 wird gesucht
und eingefügt
50
10 20 40 50
Splittoperation anwenden,
da nicht genügend Platz.
Dabei wird ein neuer
Konten erzeugt.
Prof. Dr. O. Bittel, HTWG Konstanz
40
40
10
20
50
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-121
Löschen in B-Bäumen
V remove(K key, Node<K,V> p) {
suche Schlüssel key im Baum;
if (key nicht gefunden)
return null;
if (key befindet sich im Blatt)
speichere Daten in oldValue und lösche key;
else
speichere Daten in oldValue und ersetze key (mit value)
durch nächst größeren Schlüssel key’ (mit value') und lösche key’;
// key’ befindet sich im rechten Teilbaum von key ganz links in einem Blatt
for (alle Knoten k vom aktuellen Knoten bis zur Wurzel) {
if ( Schlüsselunterlauf bei k) {
if (Geschwisterknoten kann Schlüssel abgeben)
Übernahme von Schlüsseln vom Geschwisterknoten;
else
Verschmelze Knoten k mit einem Geschwisterknoten;
}
else
return oldValue;
}
!  Schlüsselunterlauf, falls
Anzahl der Schlüssel kleiner
als ⎡m/2⎤ -1.
!  remove kann wie bei AVLBäumen rekursiv realisiert
werden.
Die Behandlung der
Schlüsselunterläufe geschieht
dann beim rekursiven Aufstieg.
!  Hat die Wurzel genau 2
Kinder, die verschmolzen
werden müssen, dann wird die
Wurzel leer und muss entfernt
werden (siehe Beispiel).
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-122
Behandlung eines Schlüsselunterlaufs: Übernahme von Schlüssel
k1 k2
Linker
Geschwisterknoten
Aktueller Knoten;
Schlüsselmenge ist
zu klein
Elternknoten
Rechter
Geschwisterknoten
!  Prüfe zuerst, ob linker Geschwisterknoten Schlüssel
abgeben kann. Falls nicht, dann prüfe rechten Knoten.
!  Abgabe von Schlüsseln so, dass Knoten in etwa gleich groß werden.
!  Verfahren kann dann abgebrochen werden,
da im Elternknoten kein Schlüsselunterlauf entstehen kann.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-123
Übernahme von Schlüssel (hier vom linken Geschwisterknoten)
...
...
...
k1 k2
...
...
...
...
Elternknoten
...
...
...
...
...
Aktueller Knoten
Linker Geschwisterknoten
Rechter Geschwisterknoten
Linker Geschwisterknoten gibt dem aktuellen Knoten
gerade soviele Schlüssel ab, so dass sich die
Knotenanzahl um maximal 1 unterscheidet.
k2
...
...
...
...
Prof. Dr. O. Bittel, HTWG Konstanz
...
...
k1
...
...
...
...
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-124
Behandlung eines Schlüsselunterlaufs: Verschmelzung
k1 k2
Linker
Geschwisterknoten
Aktueller Knoten;
Schlüsselmenge ist
zu klein
Elternknoten
Rechter
Geschwisterknoten
!  Beide Geschwisterknoten haben zu wenig Schlüssel,
um Schlüssel abgeben zu können.
!  Führe daher Verschmelzung mit einem Geschwisterknoten durch.
!  Fasse aktuellen Knoten mit linkem Geschwisterknoten (falls vorhanden) zusammen.
Sonst fasse mit rechtem Geschwisterknoten zusammen.
!  Bei Elternknoten wird ein Schlüssel nach unten verschoben.
Daher kann nun beim Elternknoten ein Schlüsselunterlauf vorkommen.
Führe daher Verfahren gegebenenfalls rekursiv beim Elternknoten fort.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-125
Verschmelzung (hier mit linkem Geschwisterknoten)
...
...
k1 k2
...
Elternknoten
...
...
...
...
Aktueller Knoten
Linker Geschwisterknoten
Rechter Geschwisterknoten
Fasse aktuellen Knoten mit
linkem Geschwisterknoten zusammen
...
...
k2
...
...
k1
...
Prof. Dr. O. Bittel, HTWG Konstanz
...
...
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-126
Beispiel 1: Löschen von 96
20
30
10
5 7
11 12 15
21 24
33
35
30
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
21 24
33
1)  Suchen und Löschen von 96
60
50
38 39
20
10
44
44
35
38 39
45
70
52 55
65 67 69 80 82 85 96
2)  Schlüsselunterlauf:
Geschwisterknoten kann
Schlüssel abgeben. Fertig!
60
50
45
90
70
52 55
85
65 67 69 80 82
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
90
SS 2017
1-127
Beispiel 2: Löschen von 80
20
30
10
5 7
11 12 15
21 24
33
35
30
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
21 24
33
60
1)  Suchen und Löschen von 80
50
38 39
20
10
44
44
35
38 39
45
70
52 55
65
80
2)  Schlüsselunterlauf:
Verschmelzung mit
Geschwisterknoten.
60
50
45
52 55
65 70
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-128
Beispiel 2: Löschen von 80 (Fortsetzung)
20
30
10
5 7
11 12 15
21 24
33
35
30
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
21 24
33
3)  Weiterer Schlüsselunterlauf:
Verschmelzung mit
Geschwisterknoten.
60
50
38 39
20
10
44
45
52 55
65 70
4)  Kein weiterer
Schlüsselunterlauf:
daher fertig!
44
35
50 60
38 39
45
52 55
65 70
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-129
Beispiel 3: Löschen von 60
20
10
11 12 15
5 7
30
21 24
33
30
5 7
11 12 15
Prof. Dr. O. Bittel, HTWG Konstanz
21 24
33
60
1)  Suchen und Löschen von 60
50
35
45
38 39
20
10
44
44
35
38 39
65
70
52 55
65 67 69 80 82
90
2)  Schüssel 60 befindet sich nicht in
Blatt: daher durch nächst größeren
Schlüssel 65 ersetzen und Schlüssel
65 löschen.
3)  Kein Schlüsselunterlauf, daher fertig!
50
45
85
70
52 55
85
65 67 69 80 82
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
90
SS 2017
1-130
Beispiel 4: Wurzel muss entfernt werden
40
1)  Suchen und Löschen von 50
20
50
2)  Schlüsselunterlauf;
Verschmelzung mit
Geschwisterknoten
20
40
3)  Schlüsselunterlauf in der Wurzel;
Wurzel entfernen
20
Prof. Dr. O. Bittel, HTWG Konstanz
40
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-131
Analyse (1)
Höhe eines B-Baumes mit n Schlüsseln:
!  Im schlechtesten Fall hat die Wurzel 2 Kinder und jeder andere Nicht-BlattKnoten ⎡m/2⎤ viele Kinder.
!  Damit ergibt sich eine maximale Höhe von (siehe [Ottmann u. Widmayer]):
h ≤ log ⎡m/2⎤ (n+1)/2
Beispiel:
!  Für m = 256 und n = 109 ergibt sich eine maximale Höhe von h ≤ 4.1
Aufwand für Suchen, Einfügen und Löschen:
!  Da die Höhe eines B-Baums durch log ⎡m/2⎤ (n+1)/2 beschränkt ist, ergibt sich
eine maximale Laufzeit von:
T(n) = O( log ⎡m/2⎤ (n) )
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-132
Analyse (2)
Speicherplatzausnutzung:
!  Eine naheliegende Implementierung eines B-Baums sieht für jeden Knoten ein
statisches Feld der Größe m für die Schlüssel und Zeiger auf die Kinder vor.
!  Da jeder Knoten (außer Wurzel) zwischen ⎡m/2⎤ -1 und m-1 viele Schlüssel
enthält, stellt sich die Frage, wie groß die Speicherplatzausnutzung ist.
!  Es gilt folgende Eigenschaft (siehe [Ottmann u. Widmayer]):
Wenn eine zufällig gewählte Folge von n Schlüsseln in einen anfangs leeren
B-Baum der Ordnung m eingefügt werden, dann ist eine
Speicherplatzausnutzung zu erwarten von:
ln(2) ≈ 69 %.
!  Falls eine absteigend oder aufsteigend sortierte Folge in einen leeren B-Baum
einfügt wird, ergibt sich eine besonders schlechte Speicherplatzausnutzung, die
aber immer noch bei ca. 50 % liegt.
!  Etwas lindern lässt sich das Problem, indem nicht bei jedem Knotenüberlauf eine
Splittoperation durchgeführt wird, sondern zuvor geprüft wird, ob Schlüssel an
Geschwisterknoten abgegeben werden können.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-133
Anwendungen
Externe Suchverfahren
!  Eine der wichtigsten Anwendungen von B-Bäume sind externe Suchverfahren (z.B.
im Datenbankbereich). Die Menge der Datensätze ist dabei so groß, dass sie nur auf
Hintergrundspeicher wie beispielsweise Festplatte abgespeichert werden kann.
!  Die Knotengröße m wird dann so gewählt, dass mit einem Festplattenzugriff die
gesamten Daten eines Knotens (d.h. Schlüssel und Zeiger; Indexseite) gelesen
werden können.
!  Die Datensätze selbst sind auf der Platte gespeichert.
Beispiel:
!  Wählt man beispielsweise m = 256 und sind ca. n = 109 Datensätze zu verwalten,
dann ergibt sich ein B-Baum etwa der Höhe 4. Speichert man die beiden obersten
Ebenen des B-Baums (d.h. Wurzel und seine Kinder) im Hauptspeicher (das sind
maximal 255 + 256*255 = 65535 Schlüssel), dann kommt man im schlechtesten Fall
bei einer Suchoperation mit 3 Plattenzugriffe aus.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-134
Variante: B*-Baum
!  Zusätzliche Forderung:
jeder Knoten (außer der Wurzel) muss mindestens zu 2/3 gefüllt sein.
!  Vorteil:
bessere Speicherplatzausnutzung und geringere Höhe des Baumes.
!  Nachteil:
bei den Operationen Einfügen und Löschen kommt ein Überschreiten der Maximalbzw. Minimalzahl der Schlüssel je Knoten im Vergleich zum B-Baum häufiger vor.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-135
Variante: B+-Baum
!  Die Datensätze werden nur in den Blättern abgespeichert.
!  Um die Bereichssuche zu unterstützen (finde alle Datensätze mit
Schlüssel k ∈ [k1, k2]), werden die Blätter miteinander verkettet.
!  In den Nicht-Blatt-Knoten stehen nur Schlüssel und Zeiger zu Navigationszwecken.
Der i-te Schlüssel ki befindet sich noch zusätzlich als kleinster Schlüssel
im Teilbaum Bi+1
!  Für die Anzahl der Schlüssel in den Blättern kann eine andere Minimal- und
Maximalzahl gelten.
21
48
8
246
8 12 15
21 24
48
66
51
51 53
87
72
66
95
72 75
87 88 89
97
95 96
97
Datensätze mit Schlüssel und Nutzdaten
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-136
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
-  AVL-Bäume
-  B-Bäume
-  2-3-4-Bäume und Rot-Schwarz-Bäume
!  Digitale Suchbäume
!  kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-137
2-3-4-Bäume
!  B-Bäume der Ordnung 4 nennt man auch 2-3-4-Bäume.
Ein Nicht-Blatt-Knoten hat 2, 3 oder 4 Kinder.
20
10
5 7
11 12 15
30
21 24
33
60
35
50
38 43 45
70
52 55
90
65 67 69 80 85
96
!  2-3-4-Bäume sind eine sehr gut geeignete Alternative zu AVL-Bäumen.
!  2-3-4-Bäume lassen sich auch als Top-Down-Variante realisieren:
beim Einfügen bzw. Löschen wird der Baum mit einer Schleife von der Wurzel in
Richtung der Blätter durchlaufen.
Dabei werden Split- bzw. Verschmelzungsoperationen prophylaktisch durchgeführt.
Eingefügt bzw. gelöscht wird nur im Blatt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-138
2-3-4-Bäume und Rot-Schwarz-Bäume (1)
!  Eine beliebte Implementierungsvariante von 2-3-4-Bäumen sind Rot-Schwarz-Bäume,
die mit einer wesentlich einfacheren Datenstruktur auskommen (wie bei binären
Suchbäumen) und außerdem top-down realisiert werden können.
Beispiel: Java-Collections und STL-Bibliothek von C++.
!  Rot-Schwarz-Bäume sind binäre Suchbäume, dessen Knoten entweder rot oder
schwarz sind und für die bestimmte Färbungsregeln gelten.
!  Ein 2-3-4-Baum wird in ein Rot-Schwarz-Baum transformiert, indem
3-Knoten (Knoten mit 3 Schlüsseln) und 2-Knoten mittels einer einfachen Regel ersetzt
werden. 1-Knoten bleiben unverändert.
k2
k1 k2 k3
k1
k3
k2
k1 k2
oder
k1
Prof. Dr. O. Bittel, HTWG Konstanz
k1
k2
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-139
2-3-4-Bäume und Rot-Schwarz-Bäume (2)
Beispiel
30
15
5 10
50
20
55
40 45
60
70
65
80 85 90
30
60
15
10
5
50
20
40
55
45
Prof. Dr. O. Bittel, HTWG Konstanz
70
65
85
80
90
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-140
Färbungsregeln von Rot-Schwarz-Bäumen
Ein Rot-Schwarz-Baum ist ein binärer Suchbaum mit folgenden Färbungsregeln:
(1)  Jeder Knoten ist entweder rot oder schwarz.
(2)  Die Wurzel ist immer schwarz.
(3)  Ein roter Knoten darf kein rotes Kind haben.
(4)  Jeder Pfad von der Wurzel zu einem Blatt hat die die gleiche Anzahl an schwarzen
Knoten.
Bemerkung:
!  Durch die 4 Färbungsregeln wird gewährleistet, dass ein Rot-Schwarz Baum mit den
Transformationsregeln von Seite 1-131 auch wieder einem 2-3-4 Baum entspricht.
!  Rot-Schwarz-Bäume werden mit derselben Datenstruktur wie binäre Suchbäume
realisiert. Jedoch wird zusätzlich ein Bit für die Farbe (rot oder schwarz) verwendet.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-141
Einfügen eines Schlüssels in einen Rot-Schwarz-Baum (1)
!  durchlaufe Rot-Schwarz-Baum rsb mit einer Schleife von seiner Wurzel bis zu
demjenigen Blatt, unter dem der neue Knoten mit Schlüssel key eingefügt werden soll;
!  füge neuen roten Knoten mit Schlüssel key ein;
q = Wurzel des Baums rsb;
while (q ist nicht das Blatt, unter dem key eingefügt werden soll) {
evtl. Farbwechsel (FW);
evtl. eine der Rotationsoperationen R, LR, L, RL;
if (key < q.key)
q = q.left;
else if (key > q.key)
q = q.right;
else
// Schlüssel bereits vorhanden; mache nichts;
}
if (key < q.key)
q.left = new RedNode(key);
else
q.right = new RedNode(key);
evtl. eine der Rotationsoperationen R, LR, L, RL;
!  da der neu eingefügte Knoten rot ist, ist die Einhaltung der Regel (4)
gewährleistet(Anzahl der schwarzen Knoten sind auf jedem Pfad identisch).
!  ein Verstoss gegen Regel (3) (keine zwei rote Knoten übereinander) wird durch
Farbwechsel und Rotationsoperationen prophylaktisch vermieden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-142
Einfügen eines Schlüssels in einen Rot-Schwarz-Baum (2)
!  Wende auf Knoten q, der zwei rote Kinder hat, einen Farbwechsel FW durch.
Falls q die Wurzel ist, dann bleibt q schwarz.
q
c1
q
FW
c2
c1
c2
!  Falls durch den Farbwechsels (FW) oder durch Einfügen eines neuen roten Knotens zwei
hintereinanderfolgende roten Knoten entstehen, dann wird eine der Rotationsoperationen
R, LR, L oder RL durchgeführt.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-143
Rotationsoperationen (1)
p
g
p
s
q
C
R
g
q
s
A
B
B
C
A
!  Rotation wird um Knoten p nach rechts durchgeführt.
p und g werden dabei umgefärbt.
!  Der Knoten s (Geschwister von p) muss schwarz sein.
Begründung in [Weiss 2007] auf Seite 501.
Daher keine weitere Verletzung der Farbregel.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-144
Rotationsoperationen (2)
g
p
q
s
LR
p
C
q
A
s
A
B1
g
B1
B2
C
B2
!  Rotation wird um Knoten p nach links und dann um q nach rechts
durchgeführt. q und g werden dabei umgefärbt.
!  Die anderen beiden Rotationsoperationen L und RL werden für den Fall, dass
p rechtes Kind von g ist, entprechend symmetrisch formuliert.
!  Der Knoten s (Geschwister von p) muss schwarz sein.
Begründung in [Weiss 2007] auf Seite 501.
Daher keine weitere Verletzung der Farbregel.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-145
Beispiel (1)
!  Einfügen von 45.
!  Der Baum wird bei der Wurzel 30 beginnend top down durchlaufen:
30, 70, 60, 50.
!  Bei 50 wird ein Farbwechsel FW durchgeführt.
30
30
70
15
10
60
20
5
50
40
Prof. Dr. O. Bittel, HTWG Konstanz
55
10
85
65
70
15
80
90
60
20
5
50
40
85
65
80
90
55
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-146
Beispiel (2)
!  Durch den Farbwechsel wird eine Rechtsrotation R notwendig
30
30
70
15
10
60
20
5
50
40
Prof. Dr. O. Bittel, HTWG Konstanz
10
85
65
60
15
80
90
5
50
20
40
70
55
65
85
80
55
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
90
1-147
Beispiel (3)
!  Der Durchlauf kann bei Knoten 50 fortgesetzt werden. Dadurch wird als nächstes
Knoten 40 besucht.
!  Der neue Knoten 45 kann rechts von 40 als roter Knoten eingefügt werden.
!  Fertig!
30
30
60
15
10
5
50
20
40
65
5
85
80
Prof. Dr. O. Bittel, HTWG Konstanz
10
70
55
60
15
90
50
20
40
70
55
45
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
65
85
80
SS 2017
90
1-148
Löschen eines Schlüssels in einem Rot-Schwarz-Baum
!  Grundsätzliche Idee: Gelöscht wird immer ein rotes Blatt.
!  Zunächst wird der zu löschende Knoten durch iterativen top-down-Durchlauf
gesucht.
!  Hat der zu löschende Knoten zwei Kinder, dann wird er durch den kleinsten Knoten
min im rechten Teilbaum ersetzt. min kann nur ein Kind haben und wird als
nächstes gelöscht.
!  Hat der zu löschende Knoten genau ein rechtes Kind, dann wird er durch den
kleinsten Knoten min im rechten Teilbaum ersetzt.
min kann höchstens ein Kind haben und wird als nächstes gelöscht.
!  Hat der zu löschende Knoten genau ein linkes Kind, dann wird es durch den
größten Knoten max im linken Teilbaum ersetzt.
max kann höchstens ein Kind haben und wird als nächstes gelöscht.
!  Das Verfahren terminiert damit, dass ein Blatt gelöscht werden muss.
Damit die Färbungsregeln erfüllt sind, muss gewährleistet sein, dass das zu
löschende Blatt immer rot ist.
!  Dies wird durch Farbwechsel und Rotationsoperationen erreicht, die beim
iterativen top-Down-Durchlauf (ähnlich wie bei beim Einfügen) durchgeführt
werden.
!  Näheres siehe in [Weiss 2007] auf Seite 506 und 507.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-149
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume (Tries)
kd-Bäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-150
Tries (1)
Problem mit den bisherigen Suchbäumen
!  Beim Suchen wird bei jedem durchlaufenen Knoten immer der komplette
Schlüssel mit dem im Knoten abgespeicherten Schlüssel verglichen.
!  Bei langen Schlüsseln kann das sehr aufwendig werden.
weiss
Suche nach „wenn“:
wer
was
ist
wenn
3 Vergleiche mit komplettem Schlüssel
wie
Tries (aus retrieval; wird wie „try“ ausgesprochen)
!  Die Daten sind bei den Blättern abgespeichert.
!  Der Schlüssel wird ziffern- bzw. zeichenweise betrachtet (daher auch der Name
digitale Suchbäume) und entschieden, in welchem Teilbaum rekursiv
weitergesucht wird.
!  Erst beim Blatt wird der komplette Schlüssel verglichen.
i
w
ist
was
weiss
a e
i
i
r
n
wenn
Prof. Dr. O. Bittel, HTWG Konstanz
Suche nach „wenn“:
wie
3 Zeichen-Vergleiche und
1 Vergleich mit komplettem Schlüssel
wer
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-151
Tries (2)
Präfixproblematik:
!  Kein Schlüssel darf Präfix (Anfangsstück) eines anderen Schlüssels sein.
!  Dies kann künstlich erreicht werden, indem jeder Schlüssel mit einem
Spezialzeichen – z.B. '$' – abgeschlossen wird.
i
w
ist$
was$
weiss$
a e
i
i
r
n
$
wen$
Prof. Dr. O. Bittel, HTWG Konstanz
wie$
n wer$
wenn$
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-152
Tries (3)
Einfügen:
!  Beim Einfügen eines neuen Schlüssels müssen evtl. mehrere neue Knoten
eingefügt werden.
i
i
w
wenn einfügen
wenden$
ist$
w
ist
e
n
d
n
wenn$
wenden$
Kontrahierte Tries
!  Eliminiere aufeinander folgende Knoten, die nur ein Kind haben.
Für eine Verzweigung ist dann im allgemeinen nicht nur 1 Zeichen,
sondern ein ganzer String relevant.
i
w
ist
e
ist
wenden$
Prof. Dr. O. Bittel, HTWG Konstanz
d
wenden$
n
d
wen
i
n
wenn$
n
wenn$
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-153
Anwendung: Indexierungsverfahren
!  Ziel: Finde alle Vorkommen eines Musters (pattern) p in einem
(typischerweiser längerem) Text t.
!  Dabei ist der Text t statisch (ändert sich nicht), so dass eine einmalige
Vorverarbeitung (Indexierung) des Textes in Betracht kommt.
!  Die eigentliche Mustersuche geschieht im Index und ist nur noch von der
Länge des Musters abhängig.
Text t
Vorverarbeitung
(Indexierung)
Muster p
Index
alle Vorkommen
von p in t
!  Ein wichtiger Ansatz für Indexierungsverfahren sind Suffixbäume
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-154
Suffixbaum
!  Ein Suffixbaum für ein Text t ist ein kontrahierter Trie,
der alle Suffixe des Textes t enthält.
!  Um die Präfixproblematik zu vermeiden (kein Suffix darf Präfix eines anderen
Suffixes sein), wird der Text mit einem Sonderzeichen '$' abgeschlossen, das
sonst nicht im Text vorkommt.
!  Jedes Blatt im Suffixbaum stellt genau ein Suffix des Textes dar.
Im Blatt wird die Position des Suffix im Text abgespeichert.
!  Beispiel:
Text t:
abababcab$
Suffixe von t:
abababcab$
bababcab$
ababcab$
babcab$
abcab
bcab
cab$
ab$
b$
Prof. Dr. O. Bittel, HTWG Konstanz
Suffixbaum des Textes t:
ab
$
ab
abcab$
0
$
cab$
7
cab$
4 8
abcab$
2
cab$
b
1
ab
cab$
6
5
cab$
3
a b a b a b c a b $ Text t
0 1 2 3 4 5 6 7 8
Position
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-155
Suche im Suffixbaum
!  Um die Positionen eines Musters p in einem Text t zu ermitteln, wird das Muster p
im Suffixbaum von t gesucht.
!  Im allgemeinen endet die Suche bei einem inneren Knoten k.
!  Alle Blätter des Teilbaums mit Wurzel k geben die gesuchten Positionen an.
!  Beispiel:
Suffixbaum des Textes t = abababcab$
Suche nach
Muster p = ab
$
ab
ab
abcab$
4
cab$
0
$
cab$
7
8
abcab$
2
cab$
b
ab
cab$
6
5
cab$
1
3
a b a b a b c a b $ Text t
0 1 2 3 4 5 6 7 8
Position
Die gesuchten Positionen sind gelb
markiert.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-156
Komplexität
!  Der Suffixbaum für ein Text t der Länge n hat genau n Blätter und maximal
n-1 innere Knoten. Der String an den Kanten des Suffixbaums sind Teile des
Textes und können durch Speicherung der Anfangs- und Endposition im Text
dargestellt werden. Die Speicherung des Suffixbaums benötigt daher O(n).
!  Die Konstruktion eines Suffixbaums für ein Text t der Länge n kann in
T = O(n) durchgeführt werden. (siehe [Ottmann und Widmayer])
!  Die Suche eines Musters p der Länge m in einem Suffixbaum benötigt T = O(m).
Sollen alle Vorkommen des Musters p im Text t ermittelt werden, dann ist
T = O(m+k) notwendig, wobei k die Anzahl der Vorkommen von p in t ist.
ab
$
ab
abcab$
0
Prof. Dr. O. Bittel, HTWG Konstanz
$
cab$
7
cab$
4 8
abcab$
2
cab$
b
1
ab
cab$
5
cab$
6
Suffixbaum des Textes
t = abababcab$ mit
8 Blättern und
5 inneren Knoten.
3
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-157
Teil I: Schlüsselbasiertes Suchen
! 
! 
! 
! 
! 
! 
! 
Dictionaries
Elementare Suchverfahren
Hashverfahren
Binäre Suchbäume
Ausgeglichene Bäume
Digitale Suchbäume
kd-Bäume (k-dimensionale Bäume)
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-158
Motivation (1)
Ziel
!  Verwaltung von Punkten bzw. Daten aus einem k-dimensionalen Raum
in einem Suchbaum
!  Hier: k = 2 (2-dimensional)
Verallgemeinerung auf beliebiges k ist dann offensichtlich.
Typische Operationen
!  (Orthogonale) Bereichssuche:
finde alle Punkte (x,y) mit xmin ≤ x ≤ xmax und ymin ≤ y ≤ ymax
!  finde denjenigen Punkt (x,y), der zu einem gegebenen Punkt (x0, y0) am nächsten
liegt (nearest neighbour search).
y
x
Prof. Dr. O. Bittel, HTWG Konstanz
Bereichssuche:
suche alle Punkte im
rot umrandeten Bereich
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-159
Motivation (2)
Beispiel: 2-dimensionale Bereichssuche bei Personendaten
!  Suche alle Personen mit Einkommen zwischen 3000 und 4000 USD und einem
Geburtsdatum zwichen 01.01.1950 (= 19500101) und 31.12.1955 (= 19551231)
Beispiel aus Berg et al.,
Computational Geometry,
Springer Verlag.
Bemerkung
!  Bereichssuche lässt sich geometrisch interpretieren.
!  Daher sind kd-Bäume in der Literatur auch oft im Bereich der
geometrischen Algorithmen (computational geometry) zu finden.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-160
kd-Baum
Definition 2d-Baum
!  ein 2d–Baum ist ein binärer Baum mit folgenden Eigenschaften:
!  jeder Knoten speichert einen 2-dimensionalen Punkt (Daten)
!  für jeden Knoten p in einer gradzahligen Tiefe gilt, dass alle Knoten im linken
Teilbaum von p eine kleinere (oder gleiche) x-Koordinate und alle Knoten im
rechten Teilbaum von p eine größere (oder gleiche) x-Koordinate haben.
!  für jeden Knoten p in einer ungradzahligen Tiefe gilt, dass alle Knoten im linken
Teilbaum von p eine kleinere (oder gleiche) y-Koordinate und alle Knoten im
rechten Teilbaum von p eine größere (oder gleiche) y-Koordinate haben.
Beispiel:
Tiefe
(x,y)
53,14
0
67,51
27,28
30,11
31,85
40,26
29,16
38,23
Prof. Dr. O. Bittel, HTWG Konstanz
7, 39
99,90
70, 3
32,29
15,61
1
2
82,64
3
73,75
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
4
SS 2017
1-161
Geometrische Interpretation eines kd-Baums
!  Die Wurzel (Tiefe 0 ist gradzahlig) teilt die Ebene vertikal in zwei Teilebenen.
!  Die beiden Kinder der Wurzel (Tiefe 1 ist ungradzahlig) teilen die beiden Teilebenen
jeweils horizontal
!  Die Enkel der Wurzel (Tiefe 2) teilen die Teilebenen wieder vertikal.
!  In der nächsten Tiefe wird wieder horizontal geteilt; usw.
A
C
B
E
A
D
B
E
C
D
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-162
kd-Baum in Java als Klasse
public class KdTree {
private class KdNode {
double [ ] data;
KdNode left;
KdNode right;
KdNode(double[ ] d, KdNode l, KdNode r) {
data = d;
left = l;
right = r;
}
}
Feld der Größe 2 für
2-dimensionalen Punkt.
private KdNode root = null;
// ...
}
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-163
2-dimensionale Bereichsuche
public void printRange(double[ ] min, double[ ] max) {
printRangeR(root, 0, min, max);
}
Gibt alle Punkte (x,y) aus, für die gilt:
min[0] <= x <= max[0] und
min[1] <= y <= max[1]
private void printRangeR(KdNode p, int komp, double[ ] min, double[ ] max) {
if (p == null)
return;
if (min[0] <= p.data[0] && p.data[0] <= max[0] && min[1] <= p.data[1] && p.data[1] <= max[1]) {
System.out.println("(" + p.data[0] + "," + p.data[1] + ")");
}
komp = 0: gradzahlige Tiefe; es wird
if (min[komp] <= p.data[komp])
nach der x-Achse geordnet.
printRangeR(p.left, 1-komp, min, max);
if (max[komp] >= p.data[komp])
komp = 1: ungradzahlige Tiefe; es
printRangeR(p.right, 1-komp, min, max);
wird nach der y-Achse geordnet.
}
E
Suche alle Punkte
im roten Bereich
Prof. Dr. O. Bittel, HTWG Konstanz
A
A
B
max
C
B
C
min
D
D
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
E
SS 2017
1-164
Konstruktion eines balanzierten 2d-Baums (1)
!  Ziel: konstruiere für ein Feld von Punkten einen balanzierten 2d-Baum.
!  Teile-und-Herrsche-Verfahren:
!  Bestimme bzgl. x (bzw. y) den Median M und ordne Punkte im Feld so um, dass in
der linken Hälfte L die Punkte <= M und der rechten Hälfte R die Punkte >= M sind.
!  Konstruiere durch rekursiven Aufruf aus L und R jeweils einen kd-Baum BL und BR
!  Baue Baum mit M als Wurzel und BL und BR als linken bzw. rechten Teilbaum.
M
Median M bzgl. x
BL
L
Prof. Dr. O. Bittel, HTWG Konstanz
BR
R
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-165
Konstruktion eines balanzierten 2d-Baums (2)
public KdTree(double[ ][ ] pts) {
root = createKdTree(pts, 0, pts.length-1, 0);
}
Konstruiere aus einem Feld pts von
2-dimensionalen Punkten einen kd-Baum
private KdNode createKdTree(double[ ][ ] pts, int li, int re, int komp) {
if (li > re)
createKdTree konstruiert aus dem Punkteteilfeld
return null;
pts[li] ... pts[re] einen balanzierten kd-Baum.
else if (li == re)
Funktion arbeitet nach dem Teil-und-Herrschereturn new KdNode(pts[li], null, null);
Prinzip.
else {
int m = (li+re)/2;
quickSelect(pts, li, re, m, komp);
quickSelect ordnet das Punkteteilfeld
pts[li] ... pts[re] um, so dass in pts[m] der Median
return
steht und links von m alle Elemente <= pts[m]
new KdNode(pts[m],
und rechts von m alle Elemente >= pts[m] sind.
createKdTree(pts, li, m-1, 1-komp),
createKdTree(pts, m+1, re, 1-komp));
}
}
Prof. Dr. O. Bittel, HTWG Konstanz
Bei komp = 0 wird bzgl. des x-Werts verglichen
und bei komp = 1 bzgl. des y-Werts.
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-166
kd-Baum mit n = 100 zufällig gewählten Punkten und Bereichsabfrage
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-167
Analyse
Bereichssuche für balanzierten kd-Baum mit n Punkten:
!  Tmax(n) = O(√n)
Konstruktion eines balanzierten kd-Baums mit n Punkten:
!  Tmax(n) = O(n log n)
!  Begründung: Teile-und-Herrsche-Vefahren,
wobei sich quickSelect in O(n) realisieren lässt (siehe z.B. [Cormen et al.]).
!  Alternativ lässt sich quickSelect auch als quickSort-Variante formulieren
(einfach zu realisieren; siehe Programmiertechnik II).
Jedoch ist dann für quickSelect nur die durchschnittliche Laufzeit O(n).
Einfügen und Löschen:
!  Einfügen und Löschen lässt sich ähnlich wie bei binären Suchbäumen realisieren.
!  Jedoch sind keine effiziente Operationen für Einfügen und Löschen bekannt,
die den Baum balanziert lassen.
!  Rotationstechnik wie bei AVL-Bäumen lässt sich hier nicht anwenden.
!  Daher: nach einer Folge von Einfüge- und Lösch-Operationen, Baum rebalanzieren
mit einer Technik wie bei der Baum-Konstruktion.
Prof. Dr. O. Bittel, HTWG Konstanz
Algorithmen und Datenstrukuren – Schlüsselbasiertes Suchen
SS 2017
1-168
Herunterladen