Kapitel 3: Dynamische Datenstrukturen (Listen, Baumstrukturen)

Werbung
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3. Dynamische Datenstrukturen
3.1 Listen (vgl. Informatik I)
public class List<K extends Comparable<K>,D> implements MyCollection<K,D>{
protected static class ListElem<K,E> { ... s.u. ...}
public class ListIterator implements Iterator<Pair<K,D>>{ ... s.u. ...}
protected ListElem<K,D> head = null;
public ListIterator iterator(){return new ListIterator();}
public void insert(K key, D cont){
head = new ListElem<K,D>(kex, cont, head);}
public D find(K key)
throws Exception{
for(Pair<K,D> val: this)
if (val.getFirst().equals(key)) return val.getSecond();
throw new Exception(key+" nicht gefunden");}
public void delete(K key){ ... s.u. ...}
}
51
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Innere Klasse ListElem
protected static class ListElem<K,E> {
ListElem<K,E> succ;
K key;
E content;
ListElem(K ky, E cont, ListElem<K,E> next){
succ = next; key = ky; content = cont;}
}
• Klasse Pair<K,T> siehe Webseite
52
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Innere Klasse ListIterator
public class ListIterator implements Iterator<Pair<K,D>>{
protected ListElem<K,D> current;
public ListIterator(){current = head;}
public boolean hasNext(){return (current != null);}
public Pair<K,D> next(){
Pair<K,D> val = new Pair<K,D>(current.key, current.content);
current = current.succ;
return val;}
public void remove(){
if (head.succ == current){head = current; return;}
ListElem<K,D> elem = head;
while (elem.succ.succ != current) elem = elem.succ;
elem.succ = current;}
}
53
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Löschen
public void delete(K key){
ListIterator iter = iterator();
Pair<K,D> value;
while (iter.hasNext()){
value = iter.next();
if (value.getFirst().equals(key)) iter.remove();}}
Aufwand der Listenoperationen:
• für insert: O(1) (bei Vermeiden von Duplikaten: O(n))
• für find, delete: O(n)
54
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2 Baumstrukturen
3.2.1 Binärer Suchbaum
Idee: für Schlüssel k in Wurzel gilt:
• k0 > k
∀k 0 im rechten Teilbaum
• k0 ≤ k
∀k 0 im linken Teilbaum
43
24
16
78
39
42
55
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Operationen im binären Suchbaum
43
43
24
24
78
42
78
insert(29,d)
16
16
39
42
24
78
delete(43)
39
29
16
42
39
29
• Operationen: Suchen, Einfügen, Löschen
• Ziel: Aufwand (im Mittel) O(log n) bei n Schlüsseln
• weitere Operationen: sortierte Ausgabe (eines Teils) der Schlüssel
56
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Binärer Suchbaum in Java
public class Tree<K extends Comparable<K>,D>
implements MyCollection<K,D>{
protected Node<K,D> root = null;
public void insert(K key, D data){
if (root == null)
root = new Node<K,D>(key, data, null, null);
else root.insertNode(key, data);}
public D find(K key) throws Exception {
if (root == null) throw new Exception("nicht gefunden");
else return root.findNode(key);}
public void delete(K key) {
if (root != null) root = root.deleteNode(key);}
public LWR<K,D> iterator(){return new LWR<K,D>(root);}
}
57
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Node
class Node<K extends Comparable<K>,D> {
K key;
D data;
Node<K,D> left;
Node<K,D> right;
Node(K k, D d, Node<K,D> l, Node<K,D> r){
key=k; data=d; left=l; right=r;}
public void insertNode(K k, D data){ ... s.u. ...}
public D findNode(K k) throws Exception { ... s.u. ...}
private Node<K,D> findMaxPred(){ ... s.u. ...}
public Node<K,D> deleteNode(K k){ ... s.u. ...}
}
58
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Node: Einfügen
public void insertNode(K k, D data) {
if (key.compareTo(k)<0)
if (right == null)
right = new Node<K,D>(k, data, null, null);
else right.insertNode(k, data);
else if (left == null)
left = new Node<K,D>(k, data, null, null);
else left.insertNode(k, data);}
59
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Node: Suchen
public D findNode(K k) throws Exception {
if (key.compareTo(k)<0)
if (right == null)
throw new Exception(k+" nicht gefunden");
else return right.findNode(k);
else if (k.compareTo(key)<0)
if (left == null)
throw new Exception(k+" nicht gefunden");
else
return left.findNode(k);
else return data;}
60
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Node: Löschen
private Node<K,D> findMaxPred() {
if (right.right == null) return this;
else return right.findMaxPred();}
public Node<K,D> deleteNode(K k){
if (key.compareTo(k)<0){
if (right != null) right = right.deleteNode(k);
return this;}
else if (k.compareTo(key)<0) {
if (left != null) left = left.deleteNode(k);
return this;}
if (left
== null) return right;
if (right == null) return left;
if (left.right == null) {left.right = right; return left;}
Node<K,D> maxPred
= left.findMaxPred();
Node<K,D> max = maxPred.right;
maxPred.right = max.left;
max.left = left;
return max;}
max.right = right;
61
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Iterator für Binärbaum
import java.util.*;
class LWR<K extends Comparable<K>,D> implements Iterator<Pair<K,D>>{
protected Stack<Node<K,D>> stack = new Stack<Node<K,D>>();
public LWR(Node<K,D> root){pushSpine(root);}
private void pushSpine(Node<K,D> current){
while (current != null){
stack.push(current);
current = current.left;}}
public boolean hasNext(){return ! stack.empty();}
public Pair<K,D> next(){
Node<K,D> current = stack.pop();
if (current.right != null) pushSpine(current.right);
return
new Pair<K,D>(current.key,current.data);}
public void remove(){throw new UnsupportedOperationException();}
}
• analog: Durchlauf in Reihenfolge WLR (Präfix), LRW (Postfix), . . .
62
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Aufwand im schlechtesten Fall
• im schlechtesten Fall degeneriert der Baum zur Liste
1
2
..
.
n
find (n), t insert (n), t delete (n) ∈ O(n)
• daher: tW
W
W
63
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Aufwand im Mittel
• o.B.d.A. Schlüssel 1, . . . , n
• Annahme: alle Permutationen als Eingabefolge gleich
wahrscheinlich
• ignoriere Löschen; beachte:
• delete löscht vorzugsweise links
• also: „Balance“ wird verschlechtert
• aber: „faireres“ delete leicht möglich
64
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Mittlere Pfadlänge wn bei Suche
(i)
• mittlere Pfadlänge wn , wenn i zuerst eingegeben:
(i)
wn =
1
i −1
n−i
·0+
· (wi−1 + 1) +
· (wn−i + 1)
n
n
n
• für beliebiges zuerst eingegebenes Element:
w0 = 0
wn =
=
=
=
1
n
·
n
P
1
n
·
i=1
n
P
i=1
(i)
für n ≥ 1
wn
( i−1
n · wi−1 +
n−1
n
+
n−1
n
+
1
n2
2
n2
·
n
P
n−i
n
· wn−i +
n−1
n )
((i − 1) · wi−1 + (n − i) · wn−i )
i=1
n−1
P
i · wi
i=1
65
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Mittlere Pfadlänge wn bei Suche (2)
zeige per Induktion: wn ≤ 4 · log2 n für n ≥ 1
√
n = 1: w1 = 0 = 4 · log2 1
1, . . . , n − 1 → n, n > 1 :
wn
=
≤
=
≤
n−1
n
+
n−1
n
+
n−1
n
+
n−1
n
+
2
n2
8
n2
n−1
P
i=1
n−1
P
i · wi
i · log2 i
i=1
bn/2c
P
8
(
n2
i=1
i · log2 i +
8
(log2 n2
n2
n−1
P
i · log2 i)
i=bn/2c+1
·
bn/2c
P
i=1
i + log2 n ·
n−1
P
i)
i=bn/2c+1
. . . (b.w.)
66
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Mittlere Pfadlänge wn bei Suche (3)
wn
8
(log2 n2
n2
·
bn/2c
P
+
8
(log2 n2
n2
·
i=1
b n2 c(b n2 c+1)
2
n−1
n
+
8
((log2
n2
=
n−1
n
−
n
n
8 b 2 c(b 2 c+1)
2
n2
≤
n−1
n
−
n
8· n−1
2 ·2
n2 ·2
n−1
n
+
=
n−1
n
=
≤
= 4 · log2 n
i + log2 n ·
n − 1) ·
+
+
i)
i=bn/2c+1
log2 n · ( (n−1)n
2
b n2 c(b n2 c+1)
2
8
n2
n−1
P
log2 n ·
−
b n2 c(b n2 c+1)
))
2
+ log2 n · ( (n−1)n
−
2
b n2 c(b n2 c+1)
))
2
(n−1)n
2
+ 4 · log2 n
2
67
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Mittlerer Aufwand für das Suchen, Einfügen und Löschen
• aus wn ≤ 4 · log2 n folgt: tafind (n) ≤ 4 · log2 n ∈ O(log n)
• bei genauerer Rechnung: tafind (n) ≈ 1.386 · log2 n
(vgl. Güting,Dieker, S. 137-141)
• analog: tAinsert (n) ∈ O(log n), tAdelete (n) ∈ O(log n)
68
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Variante: inhomogener Suchbaum
• Daten nur in Blättern; innere Knoten enthalten nur Schlüssel
• größenordnungsmäßiger Aufwand der Operationen unverändert
• Löschen einfacher
• innere Knoten benötigen weniger Speicher
→ interessant bei knappem Hauptspeicher
(Blätter ggf. auf Sekundärspeicher)
Beispiel:
42
27
(16, "Bob")
(72, "Jim")
(42, "Sam")
69
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.2 Balancierte Suchbäume
• Ziel: auch im schlechtesten Fall logarithmischer Aufwand
für Suchen, Einfügen, Löschen
• Idee: Baum bei Einfügen und Löschen „geeignet ausbalancieren“
• Ansätze
• höhenbalancierte Bäume (z. B. AVL)
• gewichtsbalancierte Bäume
• B-Baum(-Variante)
70
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.2.1 AVL-Bäume
• nach Adelson-Velskii, Landis (1962)
• höhenbalancierter, binärer Suchbaum
• Idee: für jeden Knoten unterscheiden sich
die Höhen der Teilbäume um ≤ 1
71
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Berechnung der maximalen Höhe
• konstruiere zu vorgegebener Höhe einen AVL-Baum mit
minimaler Knotenanzahl
Menge Fh der Fibonacci-Bäume der Höhe h:
• ({v }, ∅) ∈ F0
• ({v1 , v2 }, {(v1 , v2 )}) ∈ F1
• T1 = (V1 , E1 ) ∈ Fh−1 , T2 = (V2 , E2 ) ∈ Fh−2 ⇒
({v } ∪ V1 ∪ V2 , E1 ∪ E2 ∪ {(v , v1 ), (v , v2 )}) ∈ Fh
falls h ≥ 2, v1 Wurzel von T1 , v2 Wurzel von T2
• keine anderen Bäume sind Fibonacci-Bäume
72
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Fibonacci-Bäume
Minimale Knotenanzahl eines AVL-Baums
#Knoten k (h) von t ∈ Fh :
k (0) = 1
k (1) = 2
k (h) = k (h − 1) + k (h − 2) + 1 für h ≥ 2
• k (h) ist die minimale Knotenanzahl eines AVL-Baums der Höhe h
73
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Vergleich: Fibonacci-Zahlen und k (h)
fib(0) = 0
fib(1) = 1
fib(h) = fib(h − 1) + fib(h − 2) für h ≥ 2
h
0
1
2
3
4
5
6
7
8
9
10
fib(h)
0
1
1
2
3
5
8
13
21
34
55
k(h)
1
2
4
7
12
20
33
54
88
143
n ≥ k (h) = fib(h + 3) − 1 >
√
⇒ 5 · (n + 2) > Φh+3
√
⇒ logΦ ( 5(n + 2)) − 3 > h
h+3
Φ√
5
−2
(mit Φ =
232
√
1+ 5
2 ,
s. Knuth)
⇒ h ∈ O(log n)
genauer: h ≤ 1.44 · log2 n + 1
find
(n) ∈ O(log n), da Suchen wie im binären Suchbaum
damit: tW
74
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Einfügen in AVL-Baum
• beim Einfügen eines Knotens kann die Balance eines Knotens
unzulässig werden
• Balance durch eine lokale Reorganisation reparabel
• (bis auf Symmetrie) zwei unterschiedliche Situationen
y
x
h(T1)+2
−2
x
−1
T3
y
y
h(T3)+3
1
T4
z −1 oder 1
T1
T2
T3
0
h(T1)+1
T1
T2
x −2
h(T2)+3
oder
fertig!
Rechtsrotation(y)
T2
T1
0
z
Doppelrotation
links−rechts(x)
= Linksrotation(y) °
Rechtsrotation(x)
0
y −1 oder 0
T1
T2
T3
x
T3
0 oder 1
h(T2)+2
oder
h(T3)+2
T4
75
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Beispiel: AVL-Baum-Operationen
42
42
insert(28)
27
16
58
29
35
62
29
27
37
58
35
16
28
37
29
28
insert(4)
27
16
4
delete(29)
42
28
62
35
16
58
37
4
62
42
27
35
58
37
62
76
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
AVL-Baum in Java
public class AVL<K extends Comparable<K>,D> implements MyCollection<K,D>{
class Node{ // member class
K key;
D content;
Node left;
Node right;
int balance;
// -1, 0 oder 1
Node(K k, D c, Node l, Node r, int b){
key=k; content=c; left=l; right=r; balance=b;}
außerdem: insertNode, deleteNode, rotateLeft, rotateRight,
findMax, rebalanceLeft usw. s. Webseite
} // Ende von class Node
protected Node root = null;
private boolean flag = false;
// Hilfsvariable für Einfügen u. Löschen
public D find(K key) throws Exception{... analog zu Tree ...}
public void insert(K key, D data){... s. Webseite ...}
public void delete(K key){... s. Webseite ...}
public AVLLWR<K,D> iterator(){return new AVLLWR<K,D>(root);}
}
77
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Aufwand von Einfügen und Löschen
• wie gezeigt, ist die Höhe eines AVL-Baums logarithmisch
• Beobachtung: durch Reorganisation sinkt die Höhe an der
Rotationsstelle um 1
• ⇒ beim Einfügen ist höchstens eine lokale Reorganisation mit
insert (n) ∈ O(log n)
konstantem Aufwand nötig ⇒ tW
• beim Löschen ist erforderlich:
• höchstens ein Aufruf von findMax (mit Aufwand O(log n))
• an jedem (von O(log n)) besuchten Knoten höchstens eine
Reorganisation mit konstantem Aufwand:
delete (n) ∈ O(log n)
⇒ tW
78
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.2.2 B-Bäume
• von Bayer, McCreight 1970
• 2m + 1-ärer Suchbaum
• besonders wichtig für Sekundärspeicher (Knoten =
ˆ Seite)
• Varianten für Hauptspeicher: 2-3-4-Bäume, 2-3-Bäume,
Rot-Schwarz-Bäume
79
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
B-Baum der Ordnung m
Trie
kd-Baum
(m ∈ IN+ )
• jeder Knoten enthält ≤ 2m Schlüssel
• jeder Knoten außer der Wurzel enthält ≥ m Schlüssel
• jeder innere Knoten mit l Schlüsseln hat l + 1 Nachfolger
• alle Blätter haben die gleiche Höhe
Beispiel:
30
10
20
24
27
32
34
50
40
45
70
63
66
80
90
80
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
B-Baum in Java
public class Btree<K extends Comparable<K>,D>
implements MyCollection<K,D{
protected Bnode<K,D> root = null;
protected int ord;
public Btree(int n){ord = n;}
public D find(K k) throws Exception {
if (root == null) throw new Exception(k+" nicht gefunden");
return root.searchNode(k);}
public void insert(K k, D c){... s.u....}
public void delete(K k) {... s.u....}
public BBLWR<K,D> iterator(){return new BBLWR<K,D>(root);}
}
81
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Bnode
class Bnode<K extends Comparable<K>, D> {
int
count = 0;
int
ord;
Entry<K,D> entry[];
public Bnode(int n){ord = n; entry = new Entry[2*ord+2];}
D searchNode(K k) throws Exception {... s.u....}
Entry<K,D> insertNode(K k, D c) {... s.u....}
boolean deleteNode(K k){... s.u....}
private Entry<K,D> findMin(){
if (entry[0].next != null) return entry[0].next.findMin();
else return entry[1];}
private boolean underflow(int i) {... s. Webseite ...}
// ggf. Ausgleich bzw. Verschmelzen mit linkem Nachbarn
}
82
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Klasse Entry
class Entry<K extends Comparable<K>, D> {
K key;
D content;
Bnode<K,D> next;
Entry(K k, D d, Bnode<K,D> n){
key=k; content=d; next=n;}
}
83
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
B-Baum: Suchen
• suche die Position von k
count=3
für
Überlauf
frei
im betrachteten Knoten
0
• wenn k gefunden: fertig
entry
1
2
3
30
50
70
4
5
• sonst suche im
Kindknoten
“gemäß der Position”
von k weiter
84
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
B-Baum in Java: Suchen
D searchNode(K k) throws Exception {
int i=1;
// alternativ: binaere Suche
while (i<=count && entry[i].key.compareTo(k)<0) i++;
if (i<=count && entry[i].key.equals(k))
return entry[i].content;
if (entry[--i] != null) {
if (entry[i].next == null)
throw new Exception(k+" nicht gefunden");}
else throw new Exception(k+" nicht gefunden");
return entry[i].next.searchNode(k);}
85
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Einfügen in B-Bäumen
• Blatt ansteuern wie bei Suche und dort einfügen
• überlaufende Knoten teilen
• „Trenneintrag“ in Vorgängerknoten einfügen
→ ggfs. neuer Überlauf
• bei Überlauf der Wurzel: neue Wurzel über Teile der alten setzen
86
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Beispiel: Einfügen in B-Bäumen
30
10
20
24
27
32
34
50
40
70
45
63
66
80
90
insert(95)
insert(42)
30
10
20
24
27
32
40
34
50
42
70
45
63
66
80
90
95
insert(22)
40
22
10
20
30
24
27
50
32
34
42
45
63
70
66
80 90 95
87
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Einfügen in B-Baum: Klasse Btree
public void insert(K k, D c) {
if (root == null) root = new Bnode<K,D>(ord);
Entry<K,D> newEntry = root.insertNode(k, c);
if (newEntry != null) {
Bnode<K,D> oldroot = root;
root = new Bnode<K,D>(ord);
root.count = 1;
root.entry[0] = new Entry<K,D>(null, null, oldroot);
root.entry[1] = newEntry;}}
88
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Einfügen in B-Baum: Klasse Bnode
Entry<K,D> insertNode(K k, D c){
int i = 1;
Entry<K,D> newEntry;
while (i <= count && entry[i].key.compareTo(k)<0) i++;
if (entry[--i] == null) entry[i] = new Entry<K,D>(null, null, null);
if (entry[i].next != null) newEntry =entry[i].next.insertNode(k, c);
else newEntry = new Entry<K,D>(k, c, null);
if (newEntry != null) {
//newEntry an Position i+1 einfügen
for (int j = ++count; j>i+1; j--)
entry[j] = entry[j-1];
entry[i+1] = newEntry;
if (count > 2*ord){ // Knoten teilen
Bnode<K,D> brother = new Bnode<K,D>(ord);
int l = 0;
for (int j = count/2+1; j <= count; j++)
brother.entry[l++] = entry[j];
count /= 2;
brother.count = count;
return new Entry<K,D>(entry[count+1].key, entry[count+1].content,
brother);
else return null;}
else return null;
}
89
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Löschen im B-Bäumen
• Blatteintrag direkt löschbar
• gelöschten Eintrag in anderem Knoten durch nächstgrößeren
Eintrag ersetzen
• bei Unterlauf: benachbarte Knoten verschmelzen oder
Einträge von Nachbarn herüberholen
• Verschmelzen kann Unterlauf des Vorgängers auslösen
• bei Unterlauf der Wurzel wird sie gelöscht
90
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Ausgleich
60
(m=3)
−
1
...
...
20
30
40
45
50
55
2
3
4
5
6
7
−
75
8
9
80
10
50
−
1
20
30
40
45
−
55
60
75
2
3
4
5
6
7
8
9
80
10
91
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Verschmelzen (m = 2)
50
90
-
20
30
-
1
2
3
4
60
6
5
90
-
20
30
50
60
1
2
3
4
5
6
92
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Beispiel: Löschen im B-Bäumen
40
22
10
20
30
24
50
27
32
34
42
45
63
70
66
80 90
95
delete (66)
40
22
10
20
30
24
50
27
32
34
42
45
63
80
70
90
95
delete (45)
22
10
20
24
27
30
32
40
80
34
42 50 63 70
90
95
delete (40)
22
10
20
24
27
30
42
32
34
80
50 63 70
90
95
93
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Löschen in B-Baum: Klasse Btree
public void delete(K k){
if (root != null){
root.deleteNode(k);
if (root.count == 0) {
if (root.entry[0].next != null &&
root.entry[0].next.count > 0)
root = root.entry[0].next;
else if (root.entry[1].next != null &&
root.entry[1].next.count >0)
root = root.entry[1].next;
else root = null;}}}
94
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Löschen in B-Baum: Klasse Bnode
boolean deleteNode(K k){
boolean flag;
// underflow?
int i = 1;
while (i<=count && entry[i].key.compareTo(k)<0) i++;
if (i<=count && entry[i].key.equals(k)){
if (entry[i].next == null){
//lösche in Blatt
for (int j = i; j<count; j++)
entry[j] = entry[j+1];
count--;
return (count<ord);}
else {
//lösche in innerem Knoten
Entry<K,D> min = entry[i].next.findMin();
entry[i].key = min.key;
entry[i].content = min.content;
flag = entry[i].next.deleteNode(min.key);}
else if (entry[--i].next == null) return false;
else flag = entry[i].next.deleteNode(k);
if (flag) return underflow(i);
// underflow s. Webseite
else return false;}
}
95
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Aufwand
• für # Knoten v gilt: v ≤ dn/me + 1
(1)
sowie v ≥ 2 ∗ (m + 1)h−1
daher: h
(2)
≤ 1 + logm+1
v
2
≤ 1 + logm+1
dn/me+1
2
∈ O(log n) fallsm > 0
6
• z. B. bei n = 2 ∗ 10 und m = 100: h ≤ 3
• da Aufwand für Suchen, Einfügen und Löschen proportional zur Höhe:
search
insert
delete
tW
(n), tW
(n), tW
(n) ∈ O(log n)
• da jeder Knoten (außer Wurzel) mindestens m von 2m möglichen
Einträgen enthält: Speicherplatzausnutzung α ≥ 50%
(wichtig bei
Sekundärspeicher!)
• durch leichte Modifikation von insert und delete: α ≥ 66%
96
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.3 Optimale Suchbäume
• manchmal sind die Suchwahrscheinlichkeiten der Schlüssel bekannt
• in der Regel ist bei solchen Anwendungen die Menge S = {s1 , . . . , sn }
der gespeicherten Schlüssel fest (weder Einfügen noch Löschen)
(o.B.d.A. si < sj falls i < j, S ⊆ IN; s0 := −∞, sn+1 := ∞)
• Beispiel: Schlüsselworttabelle eines Compilers
• Idee: baue Suchbaum so, dass Gesamtsuchzeit minimal
pi : Wahrscheinlichkeit, dass si gesucht
(i = 1, . . . , n)
qi : Wahrscheinlichkeit, dass Schlüssel s mit si < s < si+1 gesucht
(i = 0, . . . , n)
n
X
i=1
pi +
n
X
j=0
qj = 1
97
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Zugriffswahrscheinlichkeiten und Weglänge
• Wahrscheinlichkeiten oft experimentell bestimmt
• wenn bei m Versuchen
• ai -Mal si gesucht
(i = 1, . . . , n)
• bj -Mal s ∈ (sj , sj+1 ) gesucht
• dann Annahme: pi =
ai
m,
qj =
(j = 0, . . . , n),
bj
m
• betrachte Baum T :
• sei hi : # Knoten, auf Weg von Wurzel zu si
•
hi0 :
(i = 1, . . . , n)
1+ Anzahl besuchter Knoten bei Suche nach
s ∈ (si , si+1 )
(i = 0, . . . , n)
• dann: kumulierte gewichtete Weglänge w von T
w=
n
X
i=1
• Ziel: minimiere w
ai ∗ hi +
n
X
bj ∗ hj0
j=0
98
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Bestimmung des optimalen Suchbaums
• Ansatz (dynamische Programmierung): ausgehend von optimalen
Bäumen für Teile M1 , M2 des Wertebereichs IN bestimme optimalen
Baum für M1 ∪ M2
• Notation:
Ti,j :
optimaler Suchbaum für Wertebereich (si , sj+1 )
und gespeicherte Schlüssel si+1 , . . . , sj
ci,j :
# Besuche von Ti,j
wi,j :
kumulierte gewichtete Weglänge von Ti,j
99
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Bestimmung des optimalen Suchbaums (2)
ci,j =
j
P
k =i+1
ak +
j
P
(0 ≤ i ≤ j ≤ n)
bk
k =i
wi,i = bi
wi,j = ci,j +
(i = 0, . . . , n)
minjk =i+1 (wi,k −1
+ wk ,j ) (0 ≤ i < j ≤ n)
• der optimale Suchbaum T0,n besteht aus den Teilbäumen Ti,j ,
deren wi,j “zu w0,n beitragen”
100
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Aufwand
• es gibt ≤ n2 Werte wi,j
• jeder in j − i ≤ n Schritten bestimmbar (min!) ⇒ O(n3 ) Schritte
• verbesserbar zu O(n2 ) (→ Knuth)
durch verkleinerten Suchbereich bei min-Bildung
Bemerkung:
• wenn Einfügen, Löschen und sich ändernde
Zugriffswahrscheinlichkeiten erlaubt:
→ selbstorganisierende Bäume (→ Mehlhorn, Bd. III, Kap. 6)
d.h. gewichtsbalancierte Bäume, bei denen Gewicht abh. von
Zugriffshäufigkeit
101
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Beispiel: Optimaler Suchbaum
(m = 100)
i
1
2
3
si
27 42 68
ai
10 20 30
j
0
1
2
3
(sj , sj+1 ) (−∞, 27) (27, 42) (42, 68) (68, ∞)
bj
5
5
10
20
Berechnung von ci,j und wi,j :
ci,j :
j\i
0
1
2
3
j\i
0
1
2
3
0
5
−
−
−
0
5
−
−
−
1
20
5
−
− wi,j :
1
30
5
−
−
2
50
35 10
−
2
90
50
10
−
3
100
85 60 20
3
210 155 90 20
102
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Optimaler Baum im Beispiel
T
68
0,3
30
42
−
40
40
27
−
30
30
−
−
20
20
insgesamt: w
0,3
zum Vergleich:
= 210
42
20
27
68
20
60
−
−
−
15
15
30
insgesamt: w
−
0,3
60
= 220
103
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.4 Alphabetische Bäume (Tries)
bei bisherigen Suchbäumen:
• Knoten enthalten vollständige Schlüssel
• bei langen Schlüsseln großer Platzverbrauch
Alphabetische Bäume:
• jede Kante mit einem Zeichen des Schlüssels beschriftet
• Suche, Einfügen und Löschen folgen Kanten mit gewünschter
Beschriftung
• gut, falls viele Schlüssel gleiche Präfixe haben
Beispiel:
a
f
affe
x
axt
h
k
a
a
s
u
hase
haus
kahn
o
korn
u
kuh
104
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
3.2.5 Mehrdimensionale Bäume
• wichtig in : (relationalen) Datenbanken, Data Warehouses,
Computergrafik, Geo-Informationssytemen, OLAP
• Wertebereich D := D1 × · · · × Dk
• Schlüsselmenge S = {s1 , . . . , sn } ⊂ D
105
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Anfragen
• exakte Suche (exact match query)
vi ∈ Di
search(v1 , . . . , vk )
Beispiel: search(137, ”Koetter ”)
• partielle Übereinstimmungssuche (partial match query)
vi ∈ Di ∪ {∗}
search(v1 , . . . , vk )
Beispiel: search(∗, ”Koetter ”)
• Bereichssuche (range query)
search(r1 , . . . , rk )
wobei Intervall ri = [vi , vi0 ] ⊂ Di mit vi , vi0 ∈ Di
Beispiel: search([0, 9999], [”Kaemper ”, ”Koetter ”])
weitere Operationen:
• insert(v1 , . . . , vk )
• delete(. . . )
vi ∈ Di
Parameter wie bei Anfragen
106
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
kd-Baum
• binärer Suchbaum
• meist inhomogene Variante (Daten in Blättern, in inneren Knoten nur
Schlüssel), da Löschen leichter
• Knoten v auf Stufe iv (mit iv ≥ 0) teilt Suchraum gemäß
dv ∈ D(iv
mod k )+1 :
• für jeden Schlüssel s = (x1 , . . . , xk ) im Teilbaum Lv links von v
ist x(iv
mod k )+1
≤ dv
• analog im Teilbaum Rv rechts von v : x(iv
mod k )+1
> dv
107
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Beispiel: kd-Baum
42
(k = 2)
m
27
(27,a)
g
32
(35,a) (9,x)
(50,g)
(50,x)
p
(35,n)
(33,x)
induzierte Aufteilung des Suchraums:
60
50
40
30
20
10
a
g
mn p
x
108
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Exakte Suche nach (x1 , . . . , xk )
• an innerem Knoten v suche in Lv weiter, wenn xiv
mod k +1
≤ dv
sonst suche in Rv weiter
• an Blatt vergleiche (x1 , . . . , xk ) mit Eintrag
→ Aufwand wie beim eindimensionalen Suchbaum
tAemq (n) ∈ O(log n)
falls h ∈ O(log n)
109
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Partielle Übereinstimmungs-Suche nach (x1 , . . . , xk )
• Annahme: t < k Komponenten 6= ∗
• an innerem Knoten v :
• suche in Lv weiter, wenn ∗ =
6 xiv
mod k +1
• suche in Rv weiter, wenn ∗ =
6 xiv
• suche in Lv und Rv , wenn xiv
≤ dv
mod k +1
mod k +1
> dv
=∗
• an Blatt: vergleiche Eintrag mit Suchmuster
Aufwand:
tApmq (n) ∈ O((2k −t ) k ) = O((2h )
h
k −t
k
t
) = O(n1− k )
falls h = log2 n und 0 < t < k
110
Liste
Bäume
AVL-Baum
B-Baum
Optimale Bäume
Trie
kd-Baum
Bereichsanfrage nach ([u1 , o1 ], . . . , [uk , ok ])
• an innerem Knoten v :
• suche in Lv weiter, wenn oiv
mod k +1
≤ dv
• suche in Rv weiter, wenn uiv
mod k +1
> dv
• sonst suche in Lv und Rv weiter
uiv
mod k +1
≤ dv < oiv
(dann:
mod k +1 )
• an Blatt: vergleiche Eintrag mit Suchmuster
• Aufwand abhängig von Bereichsgröße (zwischen O(log n) und O(n))
Einfügen:
tAinsert (n) ∈ O(log n)
falls h = log2 n
Löschen:
tAdelete (n) ∈ O(log n)
falls h = log2 n, Bereich klein
Variante: Quad-Tree
• jeder innere Knoten teilt Suchraum in 4 Teile (Quadranten)
111
Herunterladen