IZBRANI ALGORITMI

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