Gebalanceerde Bomen
Gebalanceerde Bomen
Gebalanceerde Bomen
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Gebalanceerde</strong> <strong>Bomen</strong><br />
algoritmen voor binaire bomen werken goed voor verschillende soorten<br />
toepassingen maar hun ‘worst case’ gedrag zorgt voor problemen<br />
balanceren is een techniek die ervoor zorgt dat het slechtste geval<br />
zich niet kan voordoen maar implementatie is niet eenvoudig<br />
1
Top-down 2-3-4 <strong>Bomen</strong><br />
om ‘worst cases’ te vermijden is flexibiliteit nodig in de datastructuren,<br />
we veronderstellen dat knopen meer dan 1 sleutel kunnen bevatten<br />
we laten 3-knopen en 4-knopen toe (die 2 en 3 sleutels kunnen bevatten)<br />
uit een 3-knoop komen 3 links: 1 naar alle records met sleutels kleiner<br />
dan beide knopen, 1 met alle records waarvan de sleutels tussen de 2<br />
knopen liggen en 1 met alle grotere sleutels<br />
uit een 4-knoop komen 4 links: een voor elk interval dat door de 3<br />
knopen gedefinieerd is<br />
2
Fig. 1 Een 2-3-4 boom.<br />
3
zoeken is eenvoudig<br />
bvb. zoeken naar G in de boom van Fig.1:<br />
volg de middenste link van de wortelknoop (G ligt tussen E en R);<br />
volg de linkse link in de volgende knoop (knoop met H, I en N) en<br />
beëindig het zoeken zonder succes<br />
4
nieuwe knoop toevoegen: na een niet succesvolle zoektocht de knoop<br />
inhaken; wanneer de zoektocht eindigt in een 2-knoop maken we er<br />
een 3-knoop van<br />
bvb. X toevoegen in Fig.1 door X (en een extra link) toe te voegen<br />
aan de knoop die S bevat<br />
analoog kan van een 3-knoop een 4-knoop gemaakt worden<br />
5
wat als er een nieuwe sleutel toegevoegd moet worden aan een 4knoop?<br />
bvb. G toevoegen aan de boom in Fig.1<br />
betere oplossing dan er een 5-knoop van te maken in Fig.2<br />
• splits de 4-knoop in 2 2-knopen en geef één van de sleutels door<br />
aan zijn ouder<br />
• voeg daarna de nieuwe sleutel toe<br />
6
Fig. 2 Toevoegen (van G) aan een 2-3-4 boom<br />
7
toevoegen van sleutels aan een boom:<br />
wanneer er een sleutel doorgegeven moet worden aan een ouder die<br />
al een 4-knoop is<br />
een methode kan zijn om ook de ouder te splitsen (maar de hogere<br />
niveaus kunnen ook 4-knopen zijn ...)<br />
een eenvoudiger methode zorgt ervoor dat geen enkele ouder van<br />
een knoop in de boom een 4-knoop is door alle 4-knopen te splitsen<br />
tijdens het afdalen in de boom<br />
8
Fig. 3 Constructie van een 2-3-4 boom voor de volledige verzameling<br />
sleutels (A S E A R C H I N G E X A M P L E)<br />
9
een 2-knoop verbonden met een 4-knoop wordt getransformeerd in<br />
een 3-knoop verbonden met 2 2-knopen<br />
een 3-knoop verbonden met een 4-knoop kan getransformeerd worden<br />
in een 4-knoop verbonden met 2 2-knopen<br />
splitsen werkt omdat niet enkel de sleutels maar ook de verwijzingen<br />
verplaatst kunnen worden<br />
voordeel: alle bewerkingen zijn lokale transformaties (zie Fig. 4)<br />
10
Fig. 4 Splitsen van 4-knopen.<br />
11
wanneer de wortel een 4-knoop wordt splitsen we hem in 3 2-knopen<br />
(we hoeven niet te wachten op de volgende invoeging omdat er geen<br />
probleem kan zijn met de ouder van de wortelknoop)<br />
enkel bij het splitsen van de wortelknoop komt er een niveau bij in de<br />
boom<br />
op die manier verkrijgen we een gebalanceerde boom<br />
12
Eig. 1 Tijdens zoeken in 2-3-4 bomen met N knopen worden niet<br />
meer dan lgN + 1 knopen bezocht.<br />
De afstand van de wortel tot elke externe knoop is immers dezelfde.<br />
13
Eig. 2 Invoegen in 2-3-4 bomen met N knopen vergt minder dan<br />
lgN + 1 keer splitsen van knopen in het slechtste geval en gemiddeld<br />
minder dan 1 knoop.<br />
In het slechtste geval zijn alle knopen op het pad van de invoeging<br />
4-knopen, die allemaal gesplitst moeten worden.<br />
14
Fig. 5 Grote 2-3-4 boom<br />
voorbeeld in Fig. 5: boom opgebouwd met een random permutatie<br />
van 95 elementen<br />
de boom bevat 9 4-knopen, waarvan er maar 1 niet op het laagste<br />
niveau zit<br />
15
Rood-zwarte bomen<br />
2-3-4 bomen kunnen als standaard bomen voorgesteld worden door 1<br />
extra bit per knoop te gebruiken<br />
we veronderstellen dat 3-knopen en 4-knopen kleine binaire bomen<br />
zijn die met ‘rode’ verbindingen samenhangen, de ‘zwarte’ verbindingen<br />
houden de 2-3-4 bomen samen (rode verbindingen zijn voorgesteld<br />
als dikke lijnen in Fig. 6)<br />
16
Fig. 6 Rood-zwart voorstelling van 3-knopen en 4-knopen<br />
17
een voorstelling van de boom in Fig. 3 (er zijn verschillende representaties<br />
mogelijk<br />
Fig. 7 Rood-zwarte boom<br />
18
de ‘schuinte’ van de 3-knopen heeft geen belang (Fig. 6)<br />
met een 2-3-4 boom komen verschillende rood-zwart bomen overeen<br />
eigenschappen:<br />
• er zijn nooit twee opeenvolgende rode verbindingen op een pad<br />
van de wortel naar een externe knoop<br />
• elk van die paden heeft een zelfde aantal zwarte verbindingen<br />
het is nog altijd mogelijk dat een pad (afwisselend rood-zwart) twee<br />
keer zo lang is als een ander (volledig zwart) maar de padlengtes zijn<br />
allemaal evenredig met logN.<br />
19
de positie van duplicaten is opmerkelijk (zie Fig.7)<br />
als we niet zouden toelaten dat duplicaten zich aan beide zijden van<br />
een knoop kunnen bevinden kan de boom sterk uit balans geraken<br />
wanneer er veel duplicaten zijn<br />
belangrijke eigenschap: de zoekprocedure voor standaard zoeken in<br />
binaire bomen werkt ongewijzigd (behalve in het geval van gedupliceerde<br />
sleutels; we kunnen niet alle knopen met een gegeven sleutel<br />
vinden door de zoekprocedure verder te zetten)<br />
20
kleuren implementeren door 1 bit veld toe te voegen aan elke knoop,<br />
(een 1 wanneer de link die naar de knoop wijst rood is, een 0 wanneer<br />
die zwart is); de zoekprocedure bekijkt dat veld nooit<br />
het balanceermechanisme veroorzaakt bijgevolg geen overlast<br />
het zoeken is efficiënter omdat de boom gebalanceerd is<br />
er is ook heel weinig overlast door de invoegprocedure (er moet alleen<br />
gehandeld worden als er een 4-knoop is)<br />
21
void Dict::insert(itemType v, infoType info)<br />
{<br />
x = head; p = head; g = head;<br />
while (x != z)<br />
{<br />
gg = g; g = p; p = x;<br />
x = (v < x-> key) ? x->l : x->r;<br />
if (x->l->b && x->r->b) split(v);<br />
}<br />
x = new node(v, info, 1, z, z);<br />
if (v < p->key) p->l = x; else p->r = x;<br />
split(v); head->r->b=black;<br />
}<br />
22
Fig. 8 Toevoegen (van Y) aan rood-zwarte boom van Fig. 7<br />
23
er is een transformatie nodig wanneer we tegenkomen:<br />
• een 2-knoop die met een 4-knoop verbonden is<br />
• een 3-knoop die met een 4-knoop verbonden is (3 verschillende<br />
mogelijkheden: Fig. 9 en Fig. 10)<br />
24
Fig. 9 Splitsen van 4-knopen met een kleurwissel<br />
25
Fig. 10 Splitsen van 4-knopen met een kleurwissel: er is rotatie nodig.<br />
26
wanneer in Fig. 8 de links y, c en gc verwijzen naar I, R en N, gebeurt<br />
de transformatie naar Fig. 11 door de veranderingen:<br />
c->l = gc->r;<br />
gc->r = c;<br />
y->r = gc<br />
er zijn 3 andere analoge gevallen: de 3-knoop kan anders georiënteerd<br />
zijn of kan zich aan de linkerkant van y bevinden (in beide oriëntaties)<br />
27
Fig. 11 Roteren van een 3-knoop in Fig. 8.<br />
28
om alle vier de gevallen te behandelen gebruiken we de zoeksleutel<br />
v om het relevante kind (c) en het relevante kleinkind (gc) terug te<br />
vinden (3-knopen worden alleen opnieuw georiënteerd wanneer het<br />
zoeken ons naar de bodem van de boom geleid heeft)<br />
eenvoudiger dan wanneer we niet enkel c en gc moeten onthouden<br />
maar ook of ze linkse of rechtse links zijn<br />
29
struct node *rotate(itemType v, struct node *y)<br />
{<br />
struct node *c, *gc;<br />
c = (v < y->key) ? y->l : y->r;<br />
if (v < c->key)<br />
{ gc = c->l; c->l = gc->r; gc->r = c; }<br />
else<br />
{ gc = c->r; c->r = gc->l; gc->l = c; }<br />
if (v < y->key) y->l = gc; else y->r = gc;<br />
return gc;<br />
}<br />
30
de functie brengt de link terug naar de top van de 3-knoop maar voert<br />
zelf de kleurwissel niet uit<br />
voor het derde geval van split (Fig. 10):<br />
• stel g rood<br />
• stel x gelijk aan rotate(v, gg)<br />
• stel x zwart<br />
hiermee is dit geval gereduceerd tot het tweede geval (waar de 3knoop<br />
goed georiënteerd was)<br />
31
voor het vierde geval (twee links georiënteerd in verschillende richtingen;<br />
Fig. 10):<br />
stel p gelijk aan rotate(v, g)<br />
de illegale 3-knoop bestaande uit 2 knopen waarnaar door p en x<br />
verwezen werd is anders georënteerd<br />
alle knopen hebben dezelfde kleur, er is geen kleurwissel nodig en we<br />
vervallen in het derde geval (leidt tot een dubbele rotatie)<br />
32
Fig. 12 Splitsen van een knoop in een rood-zwart boom.<br />
33
Fig. 12 demonstreert de uit te voeren split wanneer G wordt toegevoegd<br />
eerst is er een kleurwissel (split) om de 4-knoop die H, I en N bevat<br />
te splitsen<br />
daarna is er een dubbele rotatie nodig: eerst rond de boog tussen I<br />
en R, daarna rond de boog tussen E en I<br />
na deze wijzigingen kan G toegevoegd worden links van H (Fig 13)<br />
34
Fig. 13 Bouwen van een rood-zwarte boom.<br />
35
wanneer de wortel een 4-knoop is (invoegen in de eerste boom van<br />
Fig. 13 maakt de split procedure de wortel rood: dit komt overeen<br />
met het transformeren, samen met de dummy knoop erboven tot een<br />
3-knoop<br />
er is geen enkele reden om dat te doen; vandaar dat er een expliciet<br />
in de code staat de wortel zwart te houden<br />
36
void split(itemType v)<br />
{<br />
x->b = red; x->l->b = black; x->r->b = black;<br />
if (p->b)<br />
{<br />
g->b = red;<br />
if (vkey != vkey) p = rotate(v, g);<br />
x = rotate(v, gg);<br />
x->b = black;<br />
}<br />
}<br />
37
de split procedure zorgt ervoor dat alle kleuren correct zijn na de<br />
rotatie en plaatst x voldoende hoog in de boom om ervoor te zorgen<br />
dat we niet verloren lopen in de zoektocht na alle wijzigingen van de<br />
links<br />
38
de klasse declaratie voor rood-zwarte bomen is precies dezelfde als<br />
voor binaire zoekbomen, er is alleen een extra binair veld b in node<br />
de dummy knopen in de Dict constructor moeten op de volgende<br />
manier geïnitialiseerd worden:<br />
Dict(int max)<br />
{<br />
z = new node( 0, infoNIL, black, 0, 0);<br />
z->l = z; z->r = z;<br />
head = new node(itemMIN, 0, black, 0, z);<br />
}<br />
39
Fig. 14 Bouwen van een rood-zwarte boom.<br />
40
Eig.3 Zoeken in een rood-zwarte boom met N knopen opgebouwd<br />
met random sleutels heeft ongeveer lgN vergelijkingen nodig, en voor<br />
een invoeging is gemiddeld minder dan 1 rotatie nodig.<br />
Het is vooral voor het worst-case gedrag dat gebalanceerde bomen<br />
zo aantrekkelijk zijn.<br />
41
Fig. 15 Een rood-zwarte boom voor een ontaard geval (opgebouwd<br />
door de getallen 1 tot 95 in volgorde toe te voegen aan een lege<br />
boom).<br />
42
Eig.4 Zoeken in een rood-zwarte boom met N knopen vergt minder<br />
dan 2 lg N+2 vergelijkingen en een toevoeging vergt minder rotaties<br />
dan een kwart van de vergelijkingen.<br />
43
Andere algoritmen<br />
Er bestaan andere analoge strategieën voor het implementeren van<br />
gebalanceerde binaire bomen. Het is vooral de rotatie die de bomen<br />
balanceert.<br />
Oudste en best gekende gebalanceerde boom is de AVL boom. Die<br />
heeft de eigenschap dat de hoogte van de twee deelbomen van elke<br />
knoop ten hoogste met 1 verschilt. Wanneer deze voorwaarde overtreden<br />
wordt door het toevoegen van een knoop, dan kan er terug<br />
aan voldaan worden door te roteren.<br />
44
Een andere gekende structuur voor gebalanceerde bomen is de 2-3<br />
boom, waarin enkel 2- en 3-knopen toegelaten zijn. Toevoegen kan<br />
door het implementeren van een extra lus (voor rotaties) maar het is<br />
niet flexibel genoeg voor een goede top-down versie.<br />
45