Vaje: AVL drevesa
Implementirali bomo AVL drevesa v jeziku OCaml. AVL drevo je dvojiško iskalno drevo, v katerem se za vsako vozlišče višini levega in desnega poddrevesa razlikujeta za največ 1.
Podatkovni tip AVL drevo
V splošnem bi želeli v iskalnih drevesih hraniti poljubne podatke, ki jih lahko primerjamo po velikosti. Tu bomo nalogo poenostavili tako, da bomo v drevesih hranili le cela števila (int).
AVL drevo je:
- bodisi prazno
- bodisi vozlišče, sestavljeno iz:
- vsebine (števila),
- višine drevesa in
- levega ter desnega AVL poddrevesa.
Naloga: definirajte podatkovni tip avltree, ki opisuje AVL drevesa.
Naloga: definirajte AVL drevo t:
5
/ \
3 8
/ \
1 4
Pametni konstruktor
Naloga: definirajte funkcijo height, ki vrne višino drevesa.
Naloga: definirajte "pametna" konstruktorja leaf: int -> avltree in node: int * avltree * avltree -> avltree, ki sama poskrbita za višino. Prav vam bo prišla funkcija max: int -> int -> int, ki vrne večjega od dveh števil. Primer uporabe:
# let six = leaf 6 ;;
val six : avltree = Node (6, 1, Empty, Empty)
# let seven = leaf 7 ;;
val seven : avltree = Node (7, 1, Empty, Empty)
# let answer = node (42, six, seven) ;;
val answer : avltree =
Node (42, 2, Node (6, 1, Empty, Empty), Node (7, 1, Empty, Empty))
Naloga: s pametnimi konstruktorji definirajte AVL drevo, enako drevesu t iz prejšnje naloge.
Drevo ⇒ seznam
Naloga: definirajte funkcijo toList: avltree -> int list, ki elemente drevesa vrne kot urejen seznam števil. Za združevanje seznamov ima OCaml operator @.
Iskanje
Algoritem, ki ugotovi, ali je dani x v drevesu t:
- če je
tprazno drevo, sexne pojavi; - če je
tvozlišče z vsebinoyin poddrevesomalterr:- če je
x = y, sexpojavi; - če je
x < y, iščemo v poddrevesul; - če je
x > y, iščemo v poddrevesur.
- če je
Iskanje bomo implementirali s funkcijo search, ki naj deluje tako:
# search 5 t ;;
- : bool = true
Naloga zapišite tip funkcije search, ki ugotovi, ali drevo t vsebuje vrednost x.
Naloga definirajte funkcijo search. Za primerjanje dveh števil uporabite funkcijo cmp s predavanj, ki vrne vrednost tipa order:
type order = Less | Equal | Greater
let cmp x y =
match compare x y with
| 0 -> Equal
| r when r < 0 -> Less
| _ -> Greater
Naloga: preizkusite, ali search deluje.
Vrtenje in uravnoteženje
Pri vstavljanju ali brisanju elementov se lahko AVL drevo "pokvari": višina levega in desnega poddrevesa nekega poddrevesa se razlikujeta za več kot 1. To popravimo z vrtenjem (rotacijo) drevesa okrog korena.
Naloga: definirajte funkcijo imbalance: avltree -> int, ki vrne stopnjo neuravnoteženosti drevesa, tj. razliko med višinama levega in desnega poddrevesa.
V AVL drevesu je lahko neuravnoteženost kateregakoli vozlišča največ 1. Bolj neuravnotežena poddrevesa lahko popravimo z vrtenjem v levo oziroma desno. Vrtenje v levo oziroma desno ponazorimo z diagramom
x y
/ \ levo / \
+ y ==> x +
/a\ / \ / \ /c\
--- + + <== + + ---
/b\ /c\ desno /a\ /b\
--- --- --- ---
Naloga: definirajte funkciji rotateLeft in rotateRight, ki dano drevo zavrtita na prikazan način. Če to ni mogoče (ker je drevo npr. prazno ali list), naj funkciji vrneta nespremenjeno drevo.
Funkciji rotateLeft in rotateRight uporabimo za uravnoteženje drevesa. Ker bomo drevo uravnotežili po vsakem vstavljanju in brisanju elementa, lahko predpostavimo, da bo neuravnoteženost drevesa kvečjemu 2. Če je neuravnoteženost enaka 0, 1 ali -1, ni treba storiti ničesar. Sicer je neuravnoteženost 2 oziroma -2. V prvem primeru imamo drevo oblike
x
/ \
/ \
y +
/ \ / \
/ \ / h \
/ h+2 \ -----
/ \
---------
Glede na višini poddreves vozlišča y lahko to drevo uravnotežimo z enim ali dvema vrtenjema. V prvem primeru imamo drevo oblike
x y
/ \ / \
/ \ / \
y + + x
/ \ /h\ ==> / \ / \
+ + --- /h+1\ + +
/ \ / \ c ----- / \ /h\
/h+1\ /h+1\* a /h+1\ ---
----- ----- ----- c
a b (lahko ima višino h) b
ki ga popravimo z enim vrtenjem v desno, kot je prikazano zgoraj.
V drugem primeru imamo drevo oblike
x x z
/ \ / \ / \
/ \ / \ / \
y + z + y x
/ \ /h\ ==> / \ /h\ ==> / \ / \
+ z --- y + --- + + + +
/h\ / \ c / \ /h\ c /h\ /h\ /h\ /h\
--- + + + + --- --- --- --- ---
a /h\ /h\ /h\ /h\ b'' a b' b'' c
--- --- --- ---
b' b'' a b'
(b' ali b'' lahko ima višino h-1)
ki ga popravimo tako, da najprej levo poddrevo (s korenom y) zavrtimo v levo, nato pa celotno drevo zavrtimo v desno.
Če je neuravnoteženost drevesa enaka -2, postopamo simetrično.
Naloga: definirajte funkcijo balance: avltree -> avltree, ki uravnoteži AVL drevo na podlagi opisanih primerov.
Vstavljanje
Nov element x vstavimo v AVL drevo t po naslednjem postopku:
- če je drevo prazno, vrni list z vsebino
x; - če je
tvozlišče z vsebinoyin poddrevesomalterr:- če je
x = y, vrnit; - če je
x < y, vstavixvlin rezultat uravnoteži; - če je
x > y, vstavixvrin rezultat uravnoteži.
- če je
Naloga: definirajte funkcijo add: int -> avltree -> avltree.
Brisanje
Element x iz AVL drevesa izbrišemo tako, da ga zamenjamo z njegovim naslednikom iz poddrevesa v vozlišču x. Pri tem bomo uporabili pomožno funkcijo removeSuccessor, ki vrne novo drevo in naslednika, deluje pa tako:
- če je
tprazno drevo, sproži izjemo; - če je
tvozlišče z vsebinoxin poddrevesomalterr:- če je
lprazno drevo, vrni(r, x); - sicer izbriši naslednika iz
l, da dobiš(l', y), nato pa sestavi in uravnoteži novo drevo s korenomxter poddrevesomal'inr.
- če je
Naloga: definirajte funkcijo removeSuccessor: avltree -> avltree * int.
Element x lahko potem izbrišemo iz AVL drevesa t po naslednjem postopku:
- če je
tprazno drevo, vrnit; - če je
tvozlišče z vsebinoyin poddrevesomalinr:- če je
x < y, izbrišixiz levega poddrevesalin rezultat uravnoteži; - če je
x > y, izbrišixiz desnega poddrevesarin rezultat uravnoteži; - če je
x = y:- če je desno poddrevo prazno, vrni
l; - če je levo poddrevo prazno, vrni
r; - sicer iz
rizbriši naslednika, da dobiš(r', y'), nato pa sestavi in uravnoteži novo drevo s korenomy'ter poddrevesomalinr'.
- če je desno poddrevo prazno, vrni
- če je
Naloga: definirajte funkcijo remove: int -> avltree -> avltree.
Vir.