NIKOLA GUID IZBRANI ALGORITMI Fakulteta za elektrotehniko, računalništvo in informatiko Maribor, 2010 Kazalo 5 Rdeče-črna drevesa 5.1 Lastnosti rdeče-črnih dreves . . . . . . 5.2 Rotacije . . . . . . . . . . . . . . . . . 5.3 Vstavljanje vozlišča . . . . . . . . . . . 5.4 Odstranjevanje vozlišča . . . . . . . . . 5.4.1 Odstranjevanje rdečega vozlišča 5.4.2 Odstranjevanje črnega vozlišča . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-1 5-1 5-5 5-9 5-16 5-24 5-27 Poglavje 5 Rdeče-črna drevesa Na dvojiških iskalnih drevesih, ki smo jih spoznali v prvem letniku 1. stopnje, se izvedejo vse osnovne operacije (iskanje, vstavljanje, odstranjevanje, ipd.) v času O(h), pri čemer je h višina drevesa. Torej, če je višina drevesa velika, so operacije počasne. Rdeče-črna drevesa so ena od mnogih iskalnih drevesnih shem, ki so uravnotežena in jamčijo, da se osnovne operacije izvedejo v času O(log2 n). 5.1 Lastnosti rdeče-črnih dreves Definicija 5.1. Rdeče-črno drevo ( red-black tree) je dvojiško iskalno drevo, ki ima ob vozlišču dodatni bit, ki označuje barvo vozlišča. Ta je lahko rdeča ali črna. ♦ Izrek 5.1. Za višino rdeče - črnega drevesa velja: h ≤ 2 log2 (n + 1) − 2, kjer je log2 (n + 1) − 1 višina polnega dvojiškega drevesa. Torej višina rdeče - črnega drevesa ni več kot dvakrat večja od višine polnega drevesa. Tako je drevo približno uravnoteženo ( balanced). ♦ Vsako vozlišče drevesa vsebuje polja: barva, kljuc, leviSin, desniSin in oce. Če ne obstaja sin ali oče od vozlišča, potem ustrezni kazalec vsebuje vrednost NIL. 5-2 5.1 Lastnosti rdeče-črnih dreves Izrek 5.2. Dvojiško drevo je rdeče-črno, če izpolnjuje naslednje lastnosti: 1. Vsako vozlišče je bodisi rdeče bodisi črno. 2. Koren je črn. 3. Vsak list (NIL) je črn. 4. Če je vozlišče rdeče, so njegovi otroci črni. 5. Vsaka preprosta pot iz poljubnega vozlišča do lista vsebuje enako število črnih vozlišč. ♦ Primer rdeče-črnega drevesa prikazuje slika 5.1, na kateri so prve štiri lastnosti trivialno razvidne. Preverimo še peto lastnost. Izberimo, na primer, vozlišče d. Poti d − h − k − list, d − h − list in d − i − list vsebujejo 2 črni vozlišči. Enako bi ugotovili za katerokoli drugo vozlišče. globina 3 2 2 1 7 h 1 3 k NIL NIL 17 a 0 2 21 c 15 b 11 d 1 16 e 1 12i NIL NIL NIL NIL 1 18 f NIL NIL 1 NIL 1 1 23 g j 19 NIL NIL NIL NIL 2 3 4 5 Slika 5.1: Rdeče-črno drevo: rdeča vozlišča so v črnobeli tehniki obarvana svetlosivo, črna pa ostanejo črna. Definicija 5.2. Število črnih vozlišč na katerikoli poti iz vozlišča x (brez vozlišča x) do lista imenujemo črna višina ( black-height) in ga označimo s hb (x). ♦ 5.1 Lastnosti rdeče-črnih dreves 5-3 Vzemimo vozlišče d na sliki 5.1. Pot d − h − k − list vsebuje dve črni vozlišči, zato je črna višina vozlišča d (tj. hb (d) = 2). Pot b − d − h − k − list vsebuje tudi dve črni vozlišči, ker črno barvo izhodiščnega vozlišča b ne upoštevamo, zato je črna višina vozlišča b (tj. hb (b) = 2). Enako velja za pot b − e − list. Definicija 5.3. Črna višina rdeče-črnega drevesa je črna višina njenega korena. ♦ V primeru na sliki 5.1 je črna višina rdeče-črnega drevesa enaka: hb (koren) = hb (a) = 3. Zgled: V primeru na sliki 5.1 ima drevo 11 notranjih vozlišč in 12 listov. Vseh vozlišč je 23. Največja višina je največ: 2 log2 (n + 1) − 2 = 2 log2 (23 + 1) − 2 = 2 log2 24 − 2 ≈ 2 · 4, 58 − 2 ≈ 7. Višina na sliki 5.1 je 5, kar je manjše od 7. ♦. 5-4 5.1 Lastnosti rdeče-črnih dreves Vsi listi (NIL) rdeče-črnega drevesa so črni in jih zato običajno ne rišemo (slika 5.2). 17 15 11 16 12 7 3 21 23 18 19 8 Slika 5.2: Rdeče-črno drevo brez označenih črnih listov Izrek 5.3. Če ne upoštevamo črnih listov drevesa (NIL), ima rdeče-črno drevo naslednje dodatne lastnosti: 1. Notranje rdeče vozlišče ima lahko samo dva črna sinova. 2. Rdeče vozlišče – list nima nobenega sina. 3. Črno vozlišče – list mora imeti brata, ki je lahko črn ali rdeč. ♦ Zgled: V primeru na sliki 5.1 velja: 1. Notranje rdeče vozlišče 11 ima dva črna sinova, tj. 7 in 12. 2. Rdeče vozlišče – list, npr. 3, nima nobenega sina. 3. Črno vozlišče – list, npr. 16, ima rdečega brata, tj. vozlišče 11. 4. Črno vozlišče – list, npr. 12, ima črnega brata, tj. vozlišče 7. ♦ 5-5 5.2 Rotacije 5.2 Rotacije Rotacija (rotation) je lokalna operacija v iskalnem drevesu, ki ohranja vrstni red ključev. Na sliki 5.3 je prikazano delovanje operacije leve rotacije (zasuka), tj. LEFT-ROTATE(T, x). Leva rotacija je v bistvu pozitivni zasuk vozlišča x okoli svojega desnega sina. Na sliki 5.3a velja: kljuc[x] ≤ kljuc[y]. (5.1) Leva rotacija vozlišča x pomeni, da rotiramo vozlišče x, ki leži na levi strani vozlišča y. Od tod tudi ime leva rotacija. Vozlišči x in y se lahko nahajata kjerkoli v dvojiškem iskalnem drevesu. Črke α, β in γ predstavljajo poljubna poddrevesa. Ko izvršimo levo rotacijo na vozlišču x, predpostavljamo, da je njegov desniSin[x] = y non-NIL. Po izvršeni levi rotaciji postane vozlišče y oče vozlišču x (y = oce[x]), medtem ko je x = leviSin[y] (slika 5.3b). Po izvršeni rotaciji še vedno velja enačba 5.1. Slika 5.3: a) Začetno stanje v drevesu, b) Stanje po izvršeni operaciji LEFT-ROTATE(T, x). 5-6 5.2 Rotacije Na sliki 5.4 je prikazano delovanje operacije desne rotacije (zasuka), tj. RIGHT-ROTATE(T, x). Desna rotacija je v bistvu negativni zasuk vozlišča x okoli svojega levega sina. Na sliki 5.4a velja: kljuc[y] ≤ kljuc[x]. (5.2) Desna rotacija vozlišča x pomeni, da rotiramo vozlišče x, ki leži na desni strani vozlišča y. Od tod tudi ime desna rotacija. Vozlišči x in y se lahko nahajata kjerkoli v dvojiškem iskalnem drevesu. Črke α, β in γ predstavljajo poljubna poddrevesa. Ko izvršimo desno rotacijo na vozlišču x, predpostavljamo, da je njegov leviSin[x] = y non-NIL. Po izvršeni desni rotaciji postane vozlišče y oče vozlišču x (y = oce[x]), medtem ko je x = desniSin[y] (slika 5.4b). Po izvršeni rotaciji še vedno velja enačba 5.2. Slika 5.4: a) Začetno stanje v drevesu, b) Stanje po izvršeni operaciji RIGHT-ROTATE(T, x). Če na sliki 5.3b izvršimo desno rotacijo RIGHT-ROTATE(T, y) dobimo sliko 5.3a. Torej inverzni ukaz k ukazu LEFT-ROTATE(T, x) je ukaz RIGHT-ROTATE(T, y). 5.2 Rotacije 5-7 Psevdokod za LEFT-ROTATE(T, x) predpostavlja, da velja desniSin[x] 6= NIL. LEFT-ROTATE(T, x) 1 y ← desniSin[x] % postavi y 2 desniSin[x] ← leviSin[y] % levo poddrevo β od y % premesti v desno poddrevo od x 3 if leviSin[y] 6= NIL 4 then oce[leviSin[y]] ← x % oče od levega sina od y postane x 5 oce[y] ← oce[x] % oče od y postane oče od x 6 if oce[x] = NIL 7 then koren[T ] ← y 8 else if x = leviSin[oce[x]] 9 then leviSin[oce[x]] ← y % postavi y na mesto, % kjer je bil prej x 10 else desniSin[oce[x]] ← y 11 leviSin[y] ← x % levi sin od y postane x 12 oce[x] ← y % oče od x postane y Koda za RIGHT-ROTATE je podobna. Obe operaciji imajo časovno zahtevnost O(1). Rotacija zamenja samo kazalce, vsa ostala polja v vozlišču ostanejo enaka. Zgled: Proceduro LEFT-ROTATE(T, 3) izvedimo na drevesu na sliki 5.5a: 1. V vrstici 1 postane y = desniSin[x] = desniSin[3] = 5. 2. V vrstici 2 postane desniSin[3] = leviSin[5] = 6. 3. Ker je pogoj v vrstici 3 izpolnjen (leviSin[5] = 6 6= NIL), postane oce[6] = oce[leviSin[5]] = 3. 4. V vrstici 5 se postavi oce[5] = oce[3] = 1. 5. Ker pogoj v vrstici 6 ni izpolnjen (oce[3] = 1 6= NIL), skočimo v vrstico 8. 6. Ker pogoj v vrstici 8 ni izpolnjen (x = 3 6= leviSin[oce[3]] = 2), se izvede vrstica 10, tj. desniSin[1] = 5. 5-8 5.2 Rotacije 7. V vrstici 11 dobimo leviSin[5] = 3. 8. V vrstici 12 se postavi oce[3] = 5. Rezultat delovanja LEFT-ROTATE(T, 3) na sliki 5.5a dobimo na sliki 5.5b. Slika 5.5: Delovanje procedure LEFT-ROTATE(T, x). 5.3 Vstavljanje vozlišča 5.3 5-9 Vstavljanje vozlišča Vstavljanje vozlišča v rdeče-črno drevo z n vozlišči izvedemo s proceduro RB-INSERT v času O(ln n). Postopek je naslednji: 1. Najprej uporabimo nekoliko modificirano proceduro TREE-INSERT za vstavljanje vozlišča z v dvojiško iskalno drevo T . 2. Zatem vozlišče z pobarvamo rdeče. 3. Da zagotovimo ohranitev lastnosti rdeče-črnih dreves, ga modificiramo s ponovnim barvanjem in izvršitvijo rotacij, kar nam izvede procedura RB-INSERT-FIXUP. RB-INSERT(T, z) 1 y ← nil[T ] 2 x ← koren[T ] % začni v korenu drevesa T 3 while x 6= nil[T ] 4 do y ← x 5 if key[z] < key[x] 6 then x ← leviSin[x] 7 else x ← desniSin[x] 8 oce[z] ← y 9 if y = nil[T ] 10 then koren[T ] ← z 11 else if v < key[y] 12 then leviSin[y] ← z 13 else desniSin[y] ← z 14 leviSin[z] ← nil[T ] 15 desniSin[z] ← nil[T ] 16 barva[z] ← RDECA 17 RB-INSERT-FIXUP(T, z) Med proceduro TREE-INSERT in RB-INSERT so štiri razlike: 1. Vsi primeri NIL v TREE-INSERT so nadomeščeni z nil[T ]. 2. V vrstici 14 postavimo leviSin[z] v nil[T ], v vrstici 15 desniSin[z] v nil[T ], da ohranimo ustrezno drevesno strukturo. 3. V vrstici 16 pobarvamo vozlišče z z RDECA. 5.3 Vstavljanje vozlišča 5-10 4. Ker lahko barvanje vozlišča povzroči, da ne veljajo več lastnosti rdečečrnega drevesa, zato v vrstici 17 pokličemo proceduro RB-INSERTFIXUP(T, z). RB-INSERT-FIXUP(T, z) 1 while barva[oce[z]] = RDECA 2 do if oce[z] = leviSin[oce[oce[z]]] 3 then y ← desniSin[oce[oce[z]]] % y je stric od z 4 if barva[y] =RDECA % barva strica 5 then barva[oce[z]] ← CRNA % scenarij 1 6 barva[y] ← CRNA % scenarij 1 7 barva[oce[oce[z]]] ← RDECA % scenarij 1 8 z ← oce[oce[z]] % scenarij 1 9 else if z = desniSin[oce[z]] 10 then z ← oce[z] % scenarij 2 11 LEFT-ROTATE(T, z) % scenarij 2 12 barva[oce[z]] ← CRNA % scenarij 3 13 barva[oce[oce[z]]] ← RDECA % scenarij 3 14 RIGHT-ROTATE(T, oce[oce[z]]) % scenarij 3 15 else (isto kot stavek then, kjer zamenjavo ’desni’ z ’levi’) 16 barva[koren[T ]] ← CRNA 5.3 Vstavljanje vozlišča 5-11 Razložimo delovanje procedure RB-INSERT-FIXUP. Poglejmo tri različne scenarije: 1. Stric y od z je rdeč. Tu imamo dve možnosti. V prvi možnosti je z levi sin (slika 5.6a), v drugi možnosti pa desni sin (slika 5.6c). Značilno za oba vzorca je, da sta tako oce[z] kot stric y od vozlišča z rdeče barve. Vozlišče d predstavlja dedka (oc[oce[z]])in ta ima črno barvo. Ker na sliki 5.6a oziroma 5.6c ne velja lastnost 4, jo spremenimo v sliko 5.6b oziroma 5.6d, kjer sta oče in stric pobarvana črno, medtem ko je dedek pobarvan rdeče. Dedek postane novi z. Slika 5.6: Scenarij 1: a) z je levi sin, b) rezultat delovanja procedure RB-INSERT-FIXUP, c) z je desni sin, b) rezultat delovanja procedure RB-INSERT-FIXUP Koda za scenarij 1 spremeni barve nekaterim vozliščem, tako da ohrani lastnost 5 (vse poti navzdol od vozlišča do lista imajo enako število črnih vozlišč). Zanka while se nadaljuje z novim z = oce[oce[z]]. Če je oče od novega z tudi rdeč, ne velja več lastnost 4, zato se sproži še en cikel zanke while. 5.3 Vstavljanje vozlišča 5-12 2. Stric y od z je črn in z je desni sin. Drugi scenarij obravnava slika 5.7. Značilno za ta vzorec je, da je oce[z] od vozlišča z rdeč, medtem ko je stric y tudi črn. Vozlišče z je desni sin svojega očeta o. Vozlišče d predstavlja dedka in je pobarvano črno. Novi z postane oce[z]. Nato izvedimo levo rotacijo novega z glede na stari z. Dobimo sliko 5.7b, kjer vozlišče z postane levi sin. Torej dobimo predpogoj za scenarij 3. Slika 5.7: Scenarij 2: a) predpogoj za scenarij 2, b) rezultat delovanja procedure RB-INSERT-FIXUP 3. Stric y od z je črn in z je levi sin. Tretji scenarij obravnava slika 5.8. Značilno za ta vzorec je, da je oce[z] od vozlišča z rdeč, medtem ko je njegov stric y črn. Vozlišče z je levi sin svojega očeta. Vozlišče d predstavlja dedka in je črno. Ker na sliki 5.8a ne velja lastnost 4, jo spremenimo v sliko 5.8b. Dedek postane desni sin očeta od z in ga pobarvamo rdeče, medtem ko oce[z] postane črn. Tu uporabimo desno rotacijo dedka d glede na oce[z]. Slika 5.8: Scenarij 3: a) predpogoj za scenarij 3, b) rezultat delovanja procedure RB-INSERT-FIXUP 5-13 5.3 Vstavljanje vozlišča Zgled 1 Vzemimo, da smo v drevo vstavili vozlišče z s ključem 4 (slika 5.9). Novo drevo ni več rdeče-črno, saj oče od z ne sme biti rdeče vozlišče. Na označeni gruči vozlišč lahko izvedemo scenarij 1 in dobimo sliko 5.10, na kateri je označena spremenjena gruča vozlišč. 12 21 2 1 7 5 23 8 y 4 z Slika 5.9: V rdeče-črno drevo smo vstavili vozlišče z. Označeni vzorec je predpogoj za izvedbo scenarija 1 Slika 5.10: Rdeče-črno drevo, ki ga dobimo z uporabo scenarija 1 na sliki 5.9 Na sliki 5.10 je narisana spremenjena gruča vozlišč zaradi scenarija 1. Izbrišimo oznako za gručo in narišimo novo gručo. Tako dobimo sliko 5.11. 5-14 5.3 Vstavljanje vozlišča Novo drevo tudi še ni rdeče-črno, saj oče od vozlišče s ključem 7 ne sme biti rdeč. Na tej sliki naj bo vozlišče s ključem 7 označeno kot vozlišče z in vozlišče s ključem 21 pa kot vozlišče y (y je črni stric od vozlišča z). 12 y 21 2 1 7 5 z 23 8 4 Slika 5.11: V rdeče-črnem drevesu smo označili vzorec, na katerem lahko uporabimo scenarij 2 Po izvedbi scenarija 2 na označeni gruči v sliki 5.11 dobimo sliko 5.12, v kateri označimo spremenjeno gručo vozlišč. Slika 5.12: V rdeče-črnem drevesu smo označili vzorec, ki smo ga dobili po izvedbi scenarija 2 5-15 5.3 Vstavljanje vozlišča Slika 5.12 predstavlja že tvorjeno gručo za scenarij 3. Po izvedbi scenarija 3 dobimo sliko 5.13, v kateri označimo spremenjeno obliko gruče vozlišč. To drevo sedaj v celoti ustreza vsem pravilom rdeče-črnih dreves. 7 12 21 z y 1 5 4 8 21 23 Slika 5.13: V rdeče-črnem drevesu smo označili vzorec, ki smo ga dobili po uporabi scenarija 3 5.4 Odstranjevanje vozlišča 5.4 5-16 Odstranjevanje vozlišča Odstranjevanje vozlišča v rdeče-črnem drevesu z n vozlišči izvedemo s proceduro RB-DELETE v času O(ln n). Postopek je naslednji: 1. Najprej uporabimo nekoliko modificirano proceduro TREE-DELETE za odstranjevanje vozlišča z iz dvojiškega iskalnega drevesa T (vrstice 1–15). 2. Da zagotovimo ohranitev lastnosti rdeče-črnih dreves, ga modificiramo s ponovnim barvanjem in izvršitvijo rotacij, kar nam izvede procedura RB-DELETE-FIXUP. RB-DELETE(T, z) 1 if leviSin[z] = nil[T ] or desniSin[z] = nil[T ] 2 then y ← z 3 else y ← TREE-SUCCESSOR(z) 4 if leviSin[y] 6= nil[T ] 5 then x ← leviSin[y] % x je vozlišče, ki pride % na mesto odstranjenega vozlišča y 6 else x ← desniSin[y] 7 oce[x] ← oce[y] 8 if oce[y] = nil[T ] 9 then koren[T ] ← x 10 else if y = leviSin[oce[y]] 11 then leviSin[oce[y]] ← x 12 else desniSin[oce[y]] ← x 13 if y 6= z 14 then key[z] ← key[y] 15 % Če ima y druga polja, jih tudi kopiraj 16 if barva[y] = CRNA 17 then RB-DELETE-FIXUP(T, x) 18 return y Poglejmo na kratko, kako deluje procedura TREE-DELETE, ki odstrani vozlišče s kazalcem z. Procedura obravnava tri možnosti: 1. Če z nima naslednikov, postane sin[oce[z]] =NIL (slika 5.14a). 2. Če ima vozlišče samo enega sina, povežemo z, tako da tvorimo novo povezavo med njegovim sinom in očetom (slika 5.14b). 5.4 Odstranjevanje vozlišča 5-17 3. Če ima vozlišče z dva sinova, ga moramo zamenjati z “najmanjšim” vozliščem (tj. vozlišče z najmanjšim ključem) v desnem poddrevesu. Najmanjše vozlišče v poddrevesu dobimo, če gremo po levih sinovih, dokler ne pridemo do lista (slika 5.14c, kjer smo nadomestili vozlišče z = 2 z “najmanjšim” vozliščem v desnem poddrevesu vozlišča z, tj. z vozliščem y = 11.). 5.4 Odstranjevanje vozlišča Slika 5.14: Odstranitev vozlišča iz dvojiškega iskalnega drevesa 5-18 5.4 Odstranjevanje vozlišča 5-19 Med proceduro TREE-DELETE in RB-DELETE so tri razlike: 1. Vse reference do NIL v TREE-DELETE so nadomeščene z referencami do straže nil[T ]. 2. Test, če velja x = NIL v vrstici 7 v TREE-DELETE, je odstranjen. Prireditev oce[x] ← oce[y] se izvrši nepogojno v vrstici 7 v proceduri RB-DELETE. Če je x straža nil[T ], kazalec njegovega očeta kaže na očeta odstranjenega vozlišča y. 3. Če odstranimo vozlišče y, ki je črno, v vrstici 17 pokličemo proceduro RB-DELETE-FIXUP(T, x). RB-DELETE-FIXUP(T, x) 1 while x 6= koren[T ] and barva[x] = CRNA 2 do if x = leviSin[oce[x]] 3 then w ← desniSin[oce[x]] % w je brat od x 4 if barva[w] =RDECA % barva brata w 5 then barva[w] ← CRNA % scenarij 1 6 barva[oce[x]] ← RDECA % scenarij 1 7 LEFT-ROTATE(T, oce[x]) % scenarij 1 8 w ← desniSin[oce[x]] % scenarij 1 9 if barva[leviSin[w]] = CRNA and barva[desniSin[w]] = CRNA 10 then barva[w] ←RDECA % scenarij 2 11 x ← oce[x] % scenarij 2 12 else if barva[desniSin[w]] =CRNA 13 then barva[leviSin[w]] ←CRNA % scenarij 3 14 barva[w] ←RDECA % scenarij 3 15 RIGHT-ROTATE(T, w) % scenarij 3 16 w ← desniSin[oce[x]] % scenarij 3 17 barva[w] ← barva[oce[x]] % scenarij 4 18 barva[oce[x]] ← CRNA % scenarij 4 19 barva[desiSin[w]] ← CRNA % scenarij 4 20 LEFT-ROTATE(T, oce[x]) % scenarij 4 21 x ← koren[T ] % scenarij 4 22 else (isto kot stavek then, kjer zamenjavo ’desni’ z ’levi’) 23 barva[x] ← CRNA 5.4 Odstranjevanje vozlišča 5-20 Če je odstranjeno vozlišče y črno, se pokliče procedura RB-DELETEFIXUP. Imamo pa tri možnosti: 1. Če je y koren in če je vozlišče x, ki ga nadomesti, rdeče, se prekrši lastnost 2. Ta primer procedura RB-DELETE-FIXUP reši tako, da se izvede samo stavek v vrstici 23, v katerem x prebarva črno (preskočimo celo zanko while). 2. Če sta tako x kot oce[y] (ki je sedaj tudi oce[x]) rdeča, sta prekršeni lastnost 4 in 5. Ta primer procedura RB-DELETE-FIXUP reši tako, da se izvede samo stavek v vrstici 23, v katerem x prebarva črno (preskočimo celo zanko while). 3. Če x ni koren in če je barva od x črna, je prekršena lastnost 5. Ta primer procedura RB-DELETE-FIXUP reši, tako da se izvede zanka while. Problem lahko popravimo, če uvedemo pojem dvojne črnine. Tako ima to vozlišče dvakratno vrednost črnega številčenja. Dodatno črnino odpravimo tako, da jo potiskamo proti korenu s pomočjo rotacij, dokler: • ne pridemo do rdečega vozlišča, ki ga prebarvamo v črno, • ne pridemo do korena in na dodatno črnino enostavno pozabimo in • ne moremo izvesti rotacij oz. barvanj. Znotraj zanke while x vedno kaže na nekorensko vozlišče z dvojno črnino. V vrstici 2 določimo, če je x levi sin ali desni sin svojega očeta oce[x]. (Podali smo psevdokod za primer, v katerem je x levi sin; primer, ko je x desni sin – vrstica 22 – je simetričen). Vozlišče w je brat od x. Ker je vozlišče x dvojno črno, vozlišče w ne more biti nil[T ] (črno vozlišče ima v drevesu vedno brata, ki je lahko črn ali rdeč). Drugače, število črnih vozlišč na poti iz oce[x] do vozlišča w bi moralo biti manjše kot število črnih vozlišč na poti iz oce[x] v x. Štiri scenarije v psevdokodu opisujejo slike 5.15 do 5.18. Preden proučimo podrobno vsakega od njih, poglejmo, kako lahko preverimo, da transformacije v vsakem od scenarijev ohranjajo lastnost 5. Ključna ideja je, da je v vsakem scenariju število črnih vozlišč (vključno z dodatno črno barvo od x) iz (vključujoč) korena poddrevesa, ki kaže k vsakemu od podreves α, β, . . . , ζ, ohranjeno s transformacijo. Če lastnost 5 velja pred transformacijo, velja tudi po njej. V sliki 5.15a, ki prikazuje scenarij 1, je število 5-21 5.4 Odstranjevanje vozlišča črnih vozlišč iz korena do bodisi podrevesa α bodisi do β enako 3 pred in po transformaciji. Število črnih vozlišč do poddreves γ, δ, ε in ζ je 2 pred in po transformaciji. Poglejmo si podrobneje vse štiri scenarije: 1. Brat w od x je rdeč. D w oce[x] B x A a b C w-l g NIL a) d e w-r E oce[x] B D w E w-r z x A a w-l C b g e z d b) Slika 5.15: Rdeče-črno drevo: a) pred transformacijo, b) po transformaciji Scenarij 1 kaže slika 5.15 (vrstice 5–8 v proceduri RB-DELETE-FIXUP). Ker mora vozlišče w imeti črne sinove, lahko zamenjamo barve od w in oce[x] in zatem izvršimo levo rotacijo na oce[x], ne da bi prekršili lastnosti rdeče-črnega drevesa. Novi brat od x, ki je levi sin w-l od starega w, je sedaj črn. Po izvedbi scenarija 1 lahko vstopamo v scenarij 2, 3 ali 4, kjer je brat w vedno črn. 2. Brat w od x je črn in oba sinova od w sta črna. Scenarij 2 kaže slika 5.16 (vrstici 10–11 v proceduri RB-DELETE-FIXUP). oce[x] je originalno bodisi rdeč bodisi črn (zato je pobarvan z belo barvo). Vozlišče x ima dvojno črnino. Ker je w tudi črn, vzamemo eno črno barvo stran tako x kot w. x ima sedaj samo eno črno barvo. w postane rdeč in oce[x] pa črn ter hkrati novi x, saj vstopa v novo iteracijo zanke while. Slika 5.16: Rdeče-črno drevo: a) pred transformacijo, b) po transformaciji 5.4 Odstranjevanje vozlišča 5-22 3. Brat w od x je črn, levi sin od w je rdeč in desni sin od w je črn. Scenarij 3 kaže slika 5.17 (vrstice 13–16 v proceduri RB-DELETEFIXUP). oce[x] je lahko rdeč ali črn, zato je pobarvan z belo barvo. Lahko zamenjamo barve od w in njegovega levega sina ter zatem izvršimo desno rotacijo w, ne da bi prekršili eno od pravil rdečečrnega drevesa. Novi brat w od x je sedaj črno vozlišče z desnim rdečim sinom. Tako smo pretvorili scenarij 3 v predpogoj za scenarij 4. Slika 5.17: Rdeče-črno drevo: a) pred transformacijo, b) po transformaciji 4. Brat w od x je črn, desni sin od w je rdeč. Scenarij 4 kaže slika 5.18 (vrstice 17–21 v proceduri RB-DELETE-FIXUP). oce[x] in levi sin w-l od w sta lahko rdeča ali črna, zato sta pobarvana z belo barvo. Vozlišče x ima dvojno črnino. w dobi barvo od oce[x]. Izvedemo levo rotacijo oce[x]. Eno črno barvo od x prenesemo na oce[x], ki postane črn, vozlišče x ima samo še eno črno barvo. S postavitvijo x na koren se zanka while zaključi. Vozlišče C = w-l ohrani barvo. Slika 5.18: Rdeče-črno drevo: a) pred transformacijo, b) po transformaciji 5.4 Odstranjevanje vozlišča 5-23 Časovna analiza procedure RB-DELETE Ker je višina rdeče-črnega drevesa z n vozlišči enaka O(log n), je skupna časovna zahtevnost procedure brez klicev v RB-DELETE-FIXUP O(log n). Znotraj procedure RB-DELETE-FIXUP se scenariji 1, 2, 3 in 4 končajo, potem ko se izvrši konstantno število sprememb barv in največ tri rotacije. Scenarij 2 je edini scenarij, v katerem se ponavlja zanka while in kazalec x premika navzgor po drevesu v času največ O(log n), medtem ko se rotacije ne izvajajo. Tako procedura RB-DELETE-FIXUP porabi O(log n) časa in izvrši največ tri rotacije. Skupni čas procedure RB-DELETE je zato O(log n). 5-24 5.4 Odstranjevanje vozlišča 5.4.1 Odstranjevanje rdečega vozlišča Odstranjeno vozlišče znotraj procedure označimo z y. Na mesto odstranjenega vozlišča y pride vozlišče, ki ga označimo z x. Če je odstranjeno vozlišče y rdeče, se v splošnem ohranijo lastnosti rdeče-črnega drevesa in ni težav. Lastnosti rdečega drevesa se ne ohranijo samo v primeru odstranitve notranjega rdečega drevesa in takrat so potrebne spremembe. a) Odstranitev rdečega lista Rdeče vozlišče - list z preprosto odstranimo in preostanek rdeče-črnega drevesa ohrani vse lastnosti (slika 5.19). 7 12 21 1 4 a) 8 5 21 z 7 12 21 1 5 8 21 b) Slika 5.19: Rdeče-črno drevo: a) pred odstranitvijo rdečega lista z, b) po odstranitvi rdečega lista z 5-25 5.4 Odstranjevanje vozlišča b) Odstranitev rdečega vozlišča, ki ima dva črna lista Rdeče vozlišče, ki ni list, ima vedno dve črni vozlišči. Denimo, da želimo odstraniti rdeče vozlišče, ki ima dve črni vozlišči – lista (slika 5.20a). Rdeče vozlišče zamenjamo z desnim listom (slika 5.20b). Čeprav je rdeče vozlišče zamenjalo črno vozlišče, se ohrani barva odstranjenega vozlišča. Vozlišče x dobi barvo odstranjenega vozlišča z. Ker rdeče vozlišče 21 kot notranje vozlišče ne more imeti samo enega črnega vozlišča, zamenjamo barvi med vozliščema x in leviSin[x]. Dobimo sliko 5.20c. 7 12 z 21 1 21 x 8 5 4 a) 7 21 21 1 x 8 leviSin[x] 5 4 b) 7 21 21 1 c) 5 x 8 leviSin[x] 4 Slika 5.20: Rdeče-črno drevo: a) pred odstranitvijo rdečega vozlišča z, b) po odstranitvi rdečega vozlišča z, c) po zamenjavi barve med x in leviSin[x] 5.4 Odstranjevanje vozlišča 5-26 c) Odstranitev notranjega rdečega vozlišča Želimo odstraniti notranje rdeče vozlišče z, ki ima vedno dve črni vozlišči (slika 5.21a). To rdeče vozlišče nadomestimo z najmanjšim vozliščem v desnem poddrevesu. V našem primeru je to črno vozlišče 5 (slika 5.21b). Vozlišče x (vrednost 5) dobi barvo odstranjenega vozlišča z, tj. rdečo barvo. Rdeče vozlišče 6 mora imeti dve črni vozlišči, sedaj ima samo eno tako vozlišče, zato zamenjamo barvi vozlišč 6 in 7 (slika 5.21c). Slika 5.21: Rdeče-črno drevo: a) pred odstranitvijo vozlišča z, b) po zamenjavi vozlišča z z vozliščem x, c) z zamenjavo barv vozlišč 6 in 7 ohranimo vse lastnosti rdeče-črnega drevesa 5-27 5.4 Odstranjevanje vozlišča 5.4.2 Odstranjevanje črnega vozlišča a) Odstranitev črnega lista, katerega oče je rdeč Odstranimo črno obarvan list z (slika 5.22a). Vozlišče x, ki nadomesti vozlišče z, je NIL. Na označeni gruči vozlišč uporabimo scenarij 2 (slika 5.22b). Rezultat scenarija 2 kaže slika 5.22c. Vozlišče 21 postane rdeče, vozlišče x pa vozlišče 12, ki ima eno samo črno barvo, saj je bilo to vozlišče pred tem rdeče. Po odstranitvi črnega lista ima rdeče vozlišče 12 samo enega črnega sina (slika 5.22b). To anomalijo popravimo tako, da zamenjamo barvo med vozliščema 12 in 21 (slika 5.22c). Ta popravek ustreza scenariju 2 (vozlišče A je vozlišče x, ki je NIL, vozlišče B je 12, vozlišče 21 je črni brat od x, tj. vozlišče D, vozlišči C in E sta vozlišči NIL.) 7 12 21 1 8 z 5 21 4 a) 7 12 21 x 1 21 5 4 b) 7 x 12 21 1 c) 5 21 4 Slika 5.22: Rdeče-črno drevo: a) pred odstranitvijo črnega lista z, b) po odstranitvi lista z, c) po realizaciji scenarija 2 5-28 5.4 Odstranjevanje vozlišča b) Odstranitev črnega lista, katerega oče je črn Odstranimo črno obarvan list z (slika 5.23a). Vozlišče x je NIL. Na označeni gruči vozlišč uporabimo scenarij 2 (slika 5.23b). Rezultat scenarija 2 kaže slika 5.23c. Vozlišče 3 postane rdeče, vozlišče x pa vozlišče 2, ki ima sedaj dvojno črnino, saj je bilo to vozlišče že pred scenarijem 2 črno. 12 41 16 12 z 1 7 5 a) 10 6 3 18 14 8 17 15 13 11 9 12 41 16 12 18 14 8 x 3 10 6 7 5 17 15 13 11 9 b) 12 41 16 x 12 3 10 6 5 7 18 14 8 9 13 15 17 11 c) Slika 5.23: Rdeče-črno drevo: a) pred odstranitvijo črnega lista z, b) po odstranitvi lista z, c) po realizaciji scenarija 2 5-29 5.4 Odstranjevanje vozlišča Označena skupina vozlišč v sliki 5.24a ustreza predpogoju za scenarij 4. Vozlišče x = A = 2 ima dvojno črnino, vozlišče B = 4 je rdeče, brat od x (w = D = 8) je črn, sinova od w, tj. C = 6 in E = 10, sta oba rdeča. Vozlišče C = 6 kot w-l ohrani rdečo barvo, medtem ko vozlišče E = 10 kot w-r pa postane črno. Vozlišče w = D = 8 dobi isto barvo, kot jo je imel oce[x] = 4, tj. rdečo barvo. Po izvedbi scenarija 4 (slika 5.24b) so vozlišča 2, 4 in 10 črna, medtem ko sta vozlišči 6 in 8 rdeči. Vozlišče x = 2 ima samo eno črno barvo. Koren tega poddrevesa postane vozlišče 8. 12 oce[x] 41 16 12 x 8 w 6 w-l 3 10 w-r 7 5 a) 18 14 9 13 15 17 11 12 16 8 w oce[x] 41 12 x b) 3 10 w-r w-l 6 5 9 11 18 14 13 15 17 7 Slika 5.24: Rdeče-črno drevo: a) predpogoj za scenarij 4, b) rezultat scenarija 4 5-30 5.4 Odstranjevanje vozlišča c) Odstranitev črnega lista, katerega oče je črn Odstranimo črno obarvan list z v drevesu na slik 5.25a, ki se nekoliko razlikuje od slike 5.23a. Vozlišče x je NIL. Na označeni gruči vozlišč uporabimo scenarij 2 (slika 5.25b). Rezultat scenarija 2 kaže slika 5.25c. Vozlišče 3 postane rdeče, vozlišče x pa vozlišče 2, ki ima dve črnini, saj je bilo to vozlišče pred tem že črno. 12 41 16 12 z 1 17 15 13 7 5 a) 10 6 3 18 14 8 12 41 16 12 18 14 8 x 3 10 6 7 5 b) 12 41 x 17 15 13 16 12 10 6 3 5 18 14 8 13 15 17 7 c) Slika 5.25: Rdeče-črno drevo: a) pred odstranitvijo črnega lista z, b) po odstranitvi lista z, c) po izvedbi scenarija 2 5-31 5.4 Odstranjevanje vozlišča Označena skupina vozlišč v sliki 5.26a ustreza predpogoju za scenarij 3. Vozlišče x = A = 2 ima dvojno črnino, vozlišče B = 4 je rdeče, brat od x (w = D = 8) je črn, levi sin w-l od w, tj. C = 6, je rdeč, in desni sin w-r od w, tj. E = 10, je črn. Po izvedbi scenarija 3 (slika 5.26b) so vozlišča 2, 6 in 10 črna, medtem ko sta vozlišči 4 in 8 rdeči. Vozlišče x = 2 ima še vedno dvojno črnino. Vozlišče oce[x] = 4 ohrani rdečo barvo. 12 oce[x] 41 x 16 8 w 12 7 5 a) 10 w-r 6 w-l 3 18 14 9 15 13 17 11 12 oce[x] x 12 41 16 18 14 w-l 6 w 3 5 8 7 9 13 15 17 10 w-r b) Slika 5.26: Rdeče-črno drevo: a) pred izvedbo scenarija 3, b) po izvedbi scenarija 3 5-32 5.4 Odstranjevanje vozlišča Označena skupina vozlišč v sliki 5.27a ustreza predpogoju za scenarij 4. Vozlišče x = A = 2 ima dvojno črnino, vozlišče oce[x] = B = 4 je rdeče, brat od x (w = D = 6) je črn, levi sin w-l od w, tj. C = 5, je črn, in desni sin w-r od w, tj. E = 8, je rdeč. Vozlišče C = w-l = 5 ohrani črno barvo, medtem ko vozlišče E = w-r = 8 pa postane črno. Vozlišče w = D = 6 dobi isto barvo, kot jo je imel oce[x] = 4, tj. rdečo barvo. Po izvedbi scenarija 4 (slika 5.27b) so vozlišča 2, 4, 5 in 8 črna, medtem ko je vozlišče 4 rdeče. Vozlišče x = 2 ima še samo eno črnino. 12 oce[x] 41 16 w x 12 w-l 8 w-r 5 3 18 14 6 7 9 17 15 13 10 a) 12 w 6 oce[x] 8 w-r 41 w-l 5 x 12 16 7 9 18 14 10 13 15 17 3 b) Slika 5.27: Rdeče-črno drevo: a) pred izvedbo scenarija 4, b) po izvedbi scenarija 4 5-33 5.4 Odstranjevanje vozlišča d) Odstranitev korena in nadomestitev z rdečim vozliščem Odstranimo koren z, ki je tako ali tako vedno črno vozlišče (slika 5.28a). Koren nadomestimo z najmanjšim vozliščem v desnem poddrevesu, v našem primeru z rdečim vozliščem 13 (slika 5.28b). Po odstranitvi in zamenjavi se ohranijo lastnosti rdeče-črnega drevesa. Vozlišče y (vozlišče 12), ki ga odstranimo, je črno, vozlišče x (13), ki ga nadomešča, pa je rdeče. Ker barva[x] 6= CRNA, se zanka while ne izvede, izvede pa se zadnji stavek v vrstici 23, ki postavi barva[x] = CRNA. 12 z 41 16 12 1 7 5 a) 10 6 3 18 14 8 17 15 x 13 11 9 x 132 51 16 12 1 b) 5 15 10 6 3 7 18 14 8 9 17 11 Slika 5.28: Rdeče-črno drevo: a) pred odstranitvijo korena z, b) po odstranitvi korena z 5-34 5.4 Odstranjevanje vozlišča e) Odstranitev korena in nadomestitev s črnim vozliščem Koren nadomestimo z najmanjšim vozliščem v desnem poddrevesu, v našem primeru s črnim vozliščem 8 (slika 5.29a). Rdeče vozlišče 12 nima več levega sina. Označena gruča vozlišč predstavlja predpogoj za scenarij 4 (slika 5.29b). Po izvedenem scenariju 4 dobimo sliko 5.29c. Vozlišče w = 21 dobi barvo vozlišča oce[x] = 12, ki je rdeča, vozlišči 12 in 23 pa se pobarvata črno. z 7 12 21 1 x 8 5 21 23 4 a) x 8 12 oce[x] 21 1 w x 5 21 4 b) w-l x 23 w-r 8 21 w 21 oce[x] 1 c) 4 23 w-r 12 5 x w-l Slika 5.29: Rdeče-črno drevo: a) pred odstranitvijo korena z, b) po odstranitvi korena z, c) po izvedbi scenarija 4 5-35 5.4 Odstranjevanje vozlišča f ) Odstranitev notranjega črnega vozlišča z dvema rdečima sinovoma in nadomestitev s črnim vozliščem Želimo odstraniti črno vozlišče 8. To vozlišče nadomestimo z najmanjšim vozliščem v desnem poddrevesu, v našem primeru s črnim vozliščem 9 (slika 5.30a). Ker premaknemo črno vozlišče, se v splošnem ne ohranijo lastnosti rdeče-črnega drevesa. Na označeni gruči vozlišč uporabimo scenarij 2 (slika 5.30b). Rezultat scenarija 2 kaže slika 5.30c). Vozlišče 10 postane črno z eno samo črnino, ker je bilo prej rdeče. Vozlišče 11 pobarvamo rdeče. 12 41 16 12 1 7 5 a) 10 6 3 18 14 z 8 17 15 13 11 9 x 12 51 16 12 1 9 x 7 5 b) 10 6 3 18 14 x 9 17 15 13 11 12 51 16 12 1 c) 9 x 10 6 3 5 7 18 14 9 13 15 17 11 Slika 5.30: Rdeče-črno drevo: a) pred odstranitvijo vozlišča z, b) po odstranitvi vozlišča z, c) po izvedbi scenarija 2 5-36 5.4 Odstranjevanje vozlišča g) Odstranitev notranjega črnega vozlišča z dvema črnima sinovoma in nadomestitev z njegovim desnim sinom Želimo odstraniti črno vozlišče 2. To vozlišče nadomestimo z desnim sinom 3 (slika 5.31a). Ker premaknemo črno vozlišče, se v splošnem ne ohranijo lastnosti rdeče-črnega drevesa. Na označeni gruči vozlišč uporabimo scenarij 2 (slika 5.31b). Rezultat scenarija 2 kaže slika 5.31c. Vozlišče 3 dobi dvojno črnino, ker je bilo prej črno. Vozlišče 1 pobarvamo rdeče. 12 41 16 z1 2 1 10 6 3 x 7 5 a) 18 14 8 17 15 13 11 9 12 41 16 oce[x] 1 3 x w 1 10 6 9 7 5 18 14 8 b) 17 15 13 11 12 41 16 x 13 w 1 c) 10 6 5 7 18 14 8 9 13 15 17 11 Slika 5.31: Rdeče-črno drevo: a) pred odstranitvijo vozlišča z, b) po odstranitvi vozlišča z, c) po izvedbi scenarija 2 5-37 5.4 Odstranjevanje vozlišča Označena skupina vozlišč v sliki 5.32a ustreza predpogoju za scenarij 4. Vozlišče x = A = 3 ima dvojno črnino, vozlišče B = 4 je rdeče, brat od x (w = D = 8) je črn, sinova od w, tj. C = 6 in E = 10, sta oba rdeča. Po izvedbi scenarija 4 (slika 5.32b) so vozlišča 3, 4 in 10 črna, medtem ko sta vozlišči 6 in 8 rdeči. Vozlišče w = 8 dobi barvo oce[x] = 4, ki je rdeča. Vozlišče x = 3 ima samo eno črno barvo. Koren tega poddrevesa postane vozlišče 8. 12 oce[x] 41 16 x 8 w 13 1 w-l 6 w-r 10 7 5 9 18 14 13 15 17 11 a) 12 w 8 oce[x] 16 w-r 10 4 18 14 x 13 1 w-l 6 5 99 11 13 15 17 7 b) Slika 5.32: Rdeče-črno drevo: a) pred izvedbo scenarija 4, b) po izvedbi scenarija 4 5-38 5.4 Odstranjevanje vozlišča h) Odstranitev notranjega črnega vozlišča z dvema črnima sinovoma in nadomestitev z rdečim vozliščem Želimo odstraniti črno vozlišče 16, ki ima dva črna sinova. To vozlišče nadomestimo z najmanjšim vozliščem v desnem poddrevesu, to je z rdečim vozliščem 17 (slika 5.33a). Ker je barva[x] = RDECA, preskočimo zanko while in v ukazu v vrstici 23 postavimo barva[x] = CRNA (slika 5.33b). 12 z 41 16 12 1 7 5 a) 10 6 3 18 14 8 17 x 15 13 11 9 12 x 41 16 17 12 1 b) 10 6 3 5 7 18 14 8 9 13 15 11 Slika 5.33: Rdeče-črno drevo: a) pred odstranitvijo vozlišča z, b) po odstranitvi vozlišča z
© Copyright 2024