Vyhledávací stromy: Splay, RB a B+
LAB 12
B4M33PAL - Ing. David Pařil
Splay trees 101
Po každém přístupu "vytáhne" používaný uzel nahoru k rootu.
- Neudržuje explicitně vyvážení
- Po operaci provede
splay(x): uzelxse přes rotace přesune do rootu - Operace běží v \(O(n)\), amortizovaná složitost operací je \(O(\log n)\)
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
Existují 3 základní kroky:
Splay operace
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
P
x
1) Zig
\(x\) nemá dědečka
2) Zig-zig
\(x\) a \(p\) jsou na stejné straně vůdči \(G\)
3) Zig-zag
\(x\) a \(p\) jsou na opačné straně vůdči \(G\)
P
G
x
P
G
x
\(x\) nad \(p\)
\(p\) nad \(g\)
\(x\) nad \(p\)
\(x\) nad \(p\)
\(x\) nad \(g\)
Proč při operaci Splay nestačí jen opakovaně používat samotný Zig?
Dvojitý krok není jen rychlejší posun uzlu o dvě úrovně, ale hlavně chytřejší lokální přestavba, která dává splay stromům jejich samooptimalizační chování a amortizované záruky.
Operace na Splay stromu:
Find
- Proveď běžné BST hledání
- If \(x\) existuje \(\rightarrow\)
splay(x) - If \(x\) neexistuje \(\rightarrow\)
splay(closest)
Insert
- Vlož klíč \(x\) jako v BST
-
splay(x)do rootu
Delete
-
find(x)
if neex. \(\rightarrow\) konec
if ex. \(\rightarrow\)je root - Smaž root
- Máš L a R podstrom
L
R
Operace na Splay stromu:
Delete
-
find(x)
if neex. \(\rightarrow\) konec
if ex. \(\rightarrow\)je root - Smaž root
- Máš L a R podstrom
L
R
A) L je prázdný \(\rightarrow\) R je novým rootem
B)Jinak:
- Najdi maximum v L (nejpravější uzel)
-
splay(maximum v L)
\(\rightarrow\) stane se rootem L - Připoj R jako pravý podstrom tohoto rootu
Splay stromy:
✅ Skvělé, když se přístupy opakují (cache-like chování)
✅ Jednoduchá implementace
✅ Amortizovaně \(O(\log n)\)
Gratulace!
Stali jste se Splay mastery! 😎👍️
Příklad
Let's get splayI'n
Do nejprve prázdného stromu splay tree vkládejte postupně klíče:
Nakreslete strom po každém vložení.
2
7
1
4
3
9
5
6
Řešení
Sekvence:
2
7
1
4
3
9
5
6
Příklad
Změna hloubky splay tree
Splay tree obsahuje \(2^n-1\) klíčů s hodnotou \(1, 2, 3, ..., 2^n-1\) a je ideálně vyvážený, to jest má hloubku \(n-1\) .
Po vyhledání prvku s klíčem 1 se tento prvek přesune do kořene stromu.
Jakou hloubku bude mít výsledný strom? Řešte zvlášť pro sudé a liché \(n\).
8
Strom má \(2^n-1\) klíčů, je perfektní, tedy:
- hloubka je \(n-1\)
- klíč 1 je nejmenší \(\rightarrow\) leží v nejlevnějším listu (v hloubce \(n-1\))
Jak probíhá Splay?
- Opakujeme zig-zig nebo zig v závislosti na počtu uzlů nad námi
Pokud \(n\) je sudé, následuje závěrečný zig
Řešení
1. Zig-zig
Pro \(n=4\):
8
4
12
2
6
10
14
1
3
5
7
9
11
13
15
1. Zig-zig
Pro \(n=4\):
8
2
12
1
4
10
14
3
6
9
11
13
15
5
7
1. Zig-zig
Pro \(n=4\):
8
1
12
2
10
14
4
9
11
13
15
3
6
5
7
1. Zig-zig
2. Zig
Pro \(n=4\):
1
8
2
12
4
10
14
3
6
9
11
13
15
5
7
1. Zig-zig
Pro \(n=5\):
16
8
24
4
12
20
28
2
6
10
14
18
22
26
30
1
3
5
7
9
11
13
15
17
19
21
23
25
27
29
31
1. Zig-zig
Pro \(n=5\):
16
8
24
2
12
20
28
1
4
10
14
18
22
26
30
3
6
9
11
13
15
17
19
21
23
25
27
29
31
5
7
1. Zig-zig
Pro \(n=5\):
16
8
24
1
12
20
28
2
10
14
18
22
26
30
4
9
11
13
15
17
19
21
23
25
27
29
31
3
6
5
7
1. Zig-zig
2. Zig-zig
Pro \(n=5\):
8
1
16
2
12
24
4
10
14
20
28
3
6
9
11
13
15
18
22
26
30
5
7
17
19
21
23
25
27
29
31
1. Zig-zig
2. Zig-zig
Pro \(n=5\):
1
8
2
16
4
12
24
3
6
10
14
20
28
5
7
9
11
13
15
18
22
26
30
17
19
21
23
25
27
29
31
Řešení
Vždy:
- 1 je v kořeni
- levý podstrom je prázdný
Výsledek:
Během těchto rotací se části stromu posunou v nejhorším případě o 2 úrovně níž, než byly v ideálním stromu.
Vyjímky:
\(n=1\): strom má jen \(\{1\}\) \(\rightarrow\) hloubka 0
\(n=2\): \(\{1,2,3\}\) \(\rightarrow\) po splay vznikne řetězec \(1 - 2 - 3\) \(\rightarrow\) hloubka 2
Red-Black Tree
= vyvážené binární stromy, které si hlídají, aby výška byla \(O(\log n)\).
Platí následující pravidla:
- Každý uzel je B nebo R
- Kořen je B
- Všechny listy "NIL" jsou B
- R uzel nemá R dítě (žádné dvě R za sebou)
- Všechny cesty k NIL listům mají stejný počet B uzlů (tzv. black-height)
Operace na RB stromu:
Find
- Porovnáme klíč s aktuálním uzlem
if menší \(\rightarrow\) vlevo
if větší \(\rightarrow\) vpravo - Najdeš nebo spadneš do NIL
Insert
Delete
Operace na RB stromu:
Insert
- Vložíš nový uzel na správné místo
- Oprava barev
- Nový uzel vkládáme R (neporušíš black-height; můžeš porušit pravidlo dvou R za sebou)
- Řešíme 3 případy:
P
G
U
x
A) Taťka je B
B) Strejda je R
C) Strejda je B
P
G
U
x
P
G
U
x
P
G
U
x
A) Taťka je B
B) Strejda je R
C) Strejda je B (nebo NIL)
P
G
U
x
P
G
U
x
Všechny podmínky OK
Jsme hotovi ✅
Taťku a strejdu dej B
Dědu hoď na R
Opakuj od dědy iterat.
P
G
U
x
If \(x\) je vnitřní dítě, pak malá rotace kolem táty (aby byl vnější)
Pak rotace kolem dědy
Pak přebarvit...
P
G
U
x
P
G
U
x
x
G
U
P
Operace na RB stromu:
Find
Insert
Delete
✅
✅
❓
Operace na RB stromu:
Delete
- Smaž: (BST-style)
- Když má uzel 0 nebo 1 dítě \(\rightarrow\) smaž přímo
- Když má uzel 2 děti \(\rightarrow\) vyměníš ho s následníkem (dítě)
- Oprav barvy:
- Smažeš R: většinou v pohodě 😏
- Smažeš B: právě jsi porušil black-height 😫
Viz přednáška (4 různé případy)
Gratulace!
Právě jsi rezignoval na RB stromy! 🤷♀️
Příklad
Fun with colors
Navrhněte červenočerné obarvení daných stromů tak, aby vznikl korektní červenočerný strom.
Prázdné (NIL) listy nejsou zobrazeny
Navrhněte červenočerné obarvení daných stromů tak, aby vznikl korektní červenočerný strom.
Prázdné (NIL) listy nejsou zobrazeny
Příklad 1:
A
E
B
F
C
D
- Každý uzel je B nebo R
- Kořen je B
- Všechny listy "NIL" jsou B
- R uzel nemá R dítě (žádné dvě R za sebou)
- Všechny cesty k NIL listům mají stejný počet B uzlů
G
Příklad 2:
A
G
B
F
E
D
C
Příklad 3:
D
B
J
H
K
I
F
E
G
A
C
Příklad
One big RB tree
RB-strom má černou výšku rovnou 11.
ℹ️ Černá výška je určena jako počet černých uzlů na cestě z kořene do kteréhokoli listu (obsahujícího klíč) zmenšený o 1 (nepočítáme root).
Určete, jaký je v tomto stromu maximální možný počet:
- B uzlů
- R uzlů
- všech uzlů
Počet uzlů je maximální pokud je strom ideálně vyvážený a obsahuje R uzly na každé sudé úrovni.
Řešení
Celkem tedy: \(N_{max} = 2^0 + 2^1 + ... + 2^{21} = 2^{22} -1\) uzlů
Červené uzly jsou liché úrovně:
\(\textcolor{red}{\textbf{R}_{max}} = 2^1 + 2^3 + ... + 2^{21} = 2 \cdot (1 + 4 + 4^2 + ... + 4^{10}) = 2 \cdot \frac{4^{11}-1}{4-1}\)
Počet R uzlů
Součet prvních \(n\) členů
geometrické posloupnosti ⇱
Počet B uzlů
Červené uzly jsou sudé úrovně:
\(\textcolor{bl}{\textbf{B}_{max}} = 2^0 + 2^2 + ... + 2^{20} = (1 + 4 + 4^2 + ... + 4^{10}) = \frac{4^{11}-1}{4-1}\)
A samozřejmě platí \(\textcolor{bl}{\textbf{B}_{max}} + \textcolor{red}{\textbf{R}_{max}} = N_{max}\)
Příklad
RB-strom manuálně
RB-strom obsahuje 15 klíčů, jeho černá hloubka je 2, tj. obsahuje 10 červených uzlů.
Hodnoty klíčů jsou \(1, 2, 3, ..., 7, 8, 21, 22, ..., 26, 27\). Do stromu postupně vložíme další klíče s hodnotou \(11, 12, 13\). Nakreslete původní strom a dále strom po každém vložení.
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
Vkládáme 11
Konflikt
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
Strejda je R
Úloha typu-easy 🤌
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
Taťka a strejda změna na B
Děda změna na R
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
Konflikt
Strejda je R 👏
Přebarvení 🎨
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
Po vložení klíče 11:
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Vkládáme 12
Konflikt
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Rotace na vnějšek
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Rotace výš
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Přebarvení
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Po vložení klíče 12:
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
13
Vkládáme 13
Konflikt
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Strejda R \(\rightarrow\) barvíme 👌
13
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Strejda R \(\rightarrow\) barvíme 👌
Konflikt
13
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
Strejda B \(\rightarrow\) rotujeme 😟
13
Řešení
8
4
2
1
3
6
5
7
21
22
23
24
25
26
27
11
12
13
Po vložení klíče 13:
Hotovo ✅
Příklad
The impossible RB-tree
Rozhodněte, zda existuje příklad regulárního (každý vnitřní uzel má dva potomky) binárního stromu který nelze obarvit podle pravidel RB-stromu.
Příklad buď uveďte nebo zdůvodněte jeho nemožnost.
Řešení
Ano, takový regulární strom existuje.
Může mít 2 nebo 3 B uzly
Nedokáže mít méně než 4 B uzly
B+ stromy
B+ strom je vyvážený vyhledávací strom používaný hlavně v databázích.
- Každý uzel (kromě kořene) má "hodně" potomků / klíčů \(\rightarrow\) malá výška
- Všechny hodnoty jsou uloženy v listech 🌿
- Vnitřní uzly obsahují pouze klíče 🔑 pro navigaci...
- Listy jsou propojené do Linked-listu
- Strom je vždy vyvážený ⚖️
Struktura uzlu
10
20
30
40
Uzel...
...řádu 5:
... se 4mi klíči ...
Struktura uzlu
10
20
30
40
1
2
3
4
20
23
26
29
20 ≤
< 30
Data
Data
Data
Data
List...
List 🌿
Node
Struktura uzlu
List 🌿
Node
List 🌿
List 🌿
List 🌿
List 🌿
List 🌿
List 🌿
List 🌿
List 🌿
Operace na B+ stromu:
Find
- Proveď běžné BST hledání
Insert
- Najdeme, kam \(x\) patří
- Vložíme mezi ostatní klíče
- If list přeteče:
- Rozdělíme list na 2
- Do rodičovského uzlu vložíme první klíč R listu
- Propojíme listy
Delete
- Smaž \(x\)
- if list nemá min. počet prvků:
- Snažíme si půjčit od sourozence
- Pokud selžeme, provedem merge s využitím klíče z rodiče
Gratulace!
Vždyť už jsme to vlastně uměli... 😏
Příklad
B+ strom na Čas
Vybudujte B+ strom řádu 3 tak, že do prázdného stromu vložíte v uvedeném pořadí klíče:
32
18
31
59
20
23
24
36
60
58
15
57
51
17
16
26
42
21
43
12
Nakreslete strom po každé operaci Insert.
Na řešení máte 7 minut.
Snažte se získat co nejvíce bodů.
32
18
31
59
20
23
24
36
60
58
15
57
51
17
16
26
42
21
43
12
Vybudujte B+ strom řádu 1 tak, že do prázdného stromu vložíte v uvedeném pořadí klíče:
Nakreslete strom po každé operaci Insert.
Na řešení máte 7 minut.
Snažte se získat co nejvíce bodů.
Řešení
Sekvence:
32
18
31
59
20
23
24
36
60
58
15
57
51
17
16
26
42
21
43
12
řád 3