Binární vyhledávací strom (BVS) je binární strom, v jehož každém vrcholu $v$ je uložen unikátní klíč $k(v)$ a pro jehož každý vrchol v platí:
Pokud $a \in L(v)$, pak $k(a) < k(v)$.
Pokud $b \in R(v)$, pak $k(b) > k(v)$.
BVSShow ($v$) | $O(n)$ | Vypiš vzestupně uspořádanou posloupnost klíčů všech vrcholů $T(v)$ |
BVSMin ($v$),
BVSMax ($v$) | $O(\log n)$ | Vrať vrchol obsahující minimální (maximální) klíč v $T(v)$ |
BVSPred ($v, w$),
BVSSucc ($v, w$) | $O(\log n)$ | Vrať předchůdce (následníka) vrcholu $w$ v $T(v)$ |
BVSFind ($v, x$) | $O(\log n)$ | Vrať vrchol v $T(v)$ s klíčem $x$, pokud takový existuje |
BVSInsert ($v, x$) | $O(\log n)$ | Vlož do $T(v)$ nový vrchol s klíčem $x$, pokud v něm ještě neexistuje |
BVSDelete ($v, x$) | $O(\log n)$ | Odstraň z $T(v)$ vrchol s klíčem $x$, pokud takový existuje |
BVSShow
)
Vstup: Ukazatel na kořen v nějakého BVS $T(v)$.
Výstup: Vzestupně uspořádaná posloupnost klíčů všech vrcholů v $T(v)$.
Z definice BVS a z definice InOrder
průchodu binárním stromem plyne:
BVSShow
($v$) vypíše vzestupně uspořádané klíče vrcholů
BVS $T(v)$ v čase $O(|T(v)|)$.
Vstup: Ukazatel na kořen v nějakého BVS $T(v)$.
Výstup: Ukazatel na vrchol obsahující nejmenší klíč v $T(v)$.
Nejmenší klíč je první v posloupnosti vzestupně uspořádaných klíčů
vygenerovaných v předchozím algoritmu InOrder
průchodem a tudíž
vrchol s nejmenším klíčem je v daném BVS ten nejvíce vlevo.
Je to tudíž buď list nebo vrchol s jedním synem.
Vstup: Ukazatel na kořen $v$ nějakého BVS $T(v)$ a na nějaký jeho vrchol $w$.
Výstup: Ukazatel na předchůdce $w$ v $T(v)$.
Klíč předchůdce prvku $w$ v $T(v)$ je ve vzestupném výpisu klíčů algoritmem BVSShow levým sousedem $k(w)$.
Pokud má $w$ v $T(v)$ levý podstrom $L(w)$, pak je předchůdce $w$ jeho maximem.
V opačném případě je předchůdcem $w$ první předek, do kterého vstoupíme zprava při průchodu nahoru.
Evidentně, prvek s nejmenším klíčem předchůdce nemá, neboť nesplňuje ani jednu podmínku.
Pro $w$ = BVSMin
($v$) najděte, co vrací BVSPred
($w$)
Algoritmus vrací $\emptyset$.
Vstup: Ukazatel na kořen $v$ nějakého BVS $T(v)$ a klíč $x$.
Výstup: Ukazatel na vrchol $s$ klíčem $x$, pokud takový v $T(v)$ existuje, jinak $\emptyset$.
Korektnost plyne okamžitě z definice BVS:
V libovolném vrcholu $u$ platí, že všechny klíče v $L(u)$ jsou menší než $k(u)$ a všechny klíče v $R(u)$ jsou větší než $k(u)$.
Vstup: Ukazatel na kořen $v$ nějakého BVS $T(v)$ a klíč $x$.
Výstup: Ukazatel na kořen $v$ BVS s prvkem s klíčem $x$.
Operaci BVSInsert
lze chápat jako hledání daného klíče a
pokud klíč nenajdeme, vložíme ho na jednoznačně určenou
pozici jako nový list (předpokládáme unikátní klíče).
Z dosud uvedeného vyplývá, že danou množinu klíčů bude existovat více korektních BVS lišících se tvarem, protože výsledný tvar BVS závisí nejen na hodnotách klíčů, ale i na pořadí, v jakém jsou do BVS vkládány.
Operace BVSShow
vrátí pro všechny možné BVS nad stejnou
množinou klíčů stejnou uspořádanou posloupnost klíčů.
Nejdříve nalezneme vrchol, který obsahuje klíč, který chceme smazat.
Při mazání vrcholu může nastat několik různých případů.
Pokud takový vrchol neexistuje, necháme strom beze změny.
Jinak, pokud je nalezený vrchol listem, odtrhneme ho od stromu.
Má-li nalezený vrchol jednoho syna, nahradíme ho tímto synem.
Pokud má nalezený vrchol, nazveme ho $u$, dva syny, pak $u$ nemůžeme odstranit přímo, protože by nebylo kam připojit jeho syny.
Využijeme ale faktu, že vrchol $u$ má v BVS následníka $w$ = BVSMin
($r(u)$).
A ten má nejvýše jednoho syna, viz Slajd 8.
Klíč vrcholu u nahradíme klíčem vrcholu w.
Aplikací postupu z případu 2) nebo 3) pak odstraníme z BVS vrchol w.
Vstup: Ukazatel na kořen $v$ nějakého BVS a klíč $x$.
Výstup: Ukazatel na kořen BVS, ze kterého byl odstraněn vrchol s klíčem $x$, pokud takový vrchol existoval.
Časová složitost operací BVSFind
, BVSMin
, BVSInsert
a BVSDelete
nad BVS T($v$) je $O(h(T (v)))$.
BVS nazveme dokonale vyvážený, pokud pro každý jeho vrchol $v$ platí $|L(v)| − |R(v)| ≤ 1$.
Dokonale vyvážený BVS o velikosti $n$ má $1 + ⌊\log n⌋$ hladin a
operace BVSFind
, BVSMin
, BVSInsert
a BVSDelete
na něm tedy
mají časovou složitost $O(log n)$.
Pokud má BVS zůstávat po provedení operací BVSInsert
a BVSDelete
dokonale vyvážený, potom ať použijeme jakoukoli
implementaci zmíněných operací, bude časová složitost aspoň jedné
z nich $\Omega(n)$ pro nekonečně mnoho různých $n$.
Uvažujme dokonale vyvážený BVS s klíči 1, 2, . . . , $n$, kde $n = 2k − 1$.
Ten pak vypadá nutně takto: (BVS se všema hladinama plně zaplněnýma)
TODO obrázek Přednáška 6 slajd 20
Kořen stromu je vždy prostřední z klíčů.
Všechna lichá čísla jsou v listech stromu.
Levý a pravý podstrom mají právě $2k−1 − 1$ vrcholů a jsou tedy opět určeny jednoznačně.
Nyní provedeme následující posloupnost dvojic operací:
Po provedení $i$-té dvojice operací bude strom obsahovat hodnoty $i + 1, . . . , i + n$.
Podle toho, zda je $i$ sudé nebo liché, se budou v listech nacházet buď všechna sudá, nebo všechna lichá čísla.
Pokaždé se proto všem vrcholům změní, zda jsou listy, což ale nutně znamená upravit $\Omega(n)$ ukazatelů.
Tedy aspoň jedna z operací BVSInsert
a BVSDelete
trvá $\Omega(n)$.
$\square$