diplomsko delo - corevozlišče (roditelj) največ dva otroka. po navadi se otroka imenujeta levi in...
TRANSCRIPT
UNIVERZA V MARIBORU
FAKULTETA ZA NARAVOSLOVJE IN MATEMATIKO
Oddelek za matematiko in računalništvo
DIPLOMSKO DELO
Mateja Oletič
Maribor, 2010
UNIVERZA V MARIBORU
FAKULTETA ZA NARAVOSLOVJE IN MATEMATIKO
Oddelek za matematiko in računalništvo
Diplomsko delo
BAZNA DREVESA
Mentor: Kandidatka:
red. prof. dr. Aleksander Vesel Mateja Oletič
Maribor, 2010
Zahvala
"Zmagovalec boste, če se nikoli ne boste predali. Ko vam je najtežje,
poskusite še enkrat." - James J. Corbett
Zahvaljujem se profesorju in mentorju red. prof. dr. Aleksandru Veselu za
strokovno pomoč in nasvete pri oblikovanju diplomskega dela.
Posebna zahvala gre staršem in sestri, ker nikoli niso podvomili vame in ker so
vedno verjeli vame. Hvala staršem za vso potrpeţljivost in finančno podporo, ki
so mi jo nudili v letih mojega študija. Vsega, kar sta mi omogočila, jima ne
morem vrniti, vendar vem, da se bosta z mano veselila dneva, ko bom v rokah
ponosno drţala diplomo.
Hvala tudi tebi Sašo, ki si me sprejel takšno kot sem in si me optimistično
spodbujal ter mi stal ob strani pri vseh mojih vzponih in padcih.
UNIVERZA V MARIBORU
FAKULTETA ZA NARAVOSLOVJE IN MATEMATIKO
IZJAVA
Podpisana Mateja Oletič, rojena 08.08.1986, študentka Fakultete za naravoslovje
in matematiko Univerze v Mariboru, smer računalništvo in sociologija, izjavljam,
da je diplomsko delo z naslovom
BAZNA DREVESA
pri mentorju red. prof. dr. Aleksandru Veselu, avtorsko delo. V diplomskem delu
so uporabljeni viri in literatura korektno navedeni; teksti in druge oblike zapisov
niso uporabljeni brez navedbe avtorjev.
__________________________
Maribor, 15.10.2010
Program diplomskega dela
Bazna drevesa
Bazno drevo je podatkovna struktura, v kateri so podatki urejeni glede na
primerjavo istoleţnih števk ključev predstavljenih v izbrani bazi. Predstavljena naj
bo splošna definicija in osnovne oblike baznih dreves. Opisani naj bodo algoritmi
za iskanje, vstavljanje in brisanje podatkov. Ena od oblik baznega drevesa naj bo
realizirana v programskem jeziku.
Osnovna literatura:
Parsons, T. W. Introduction to algorithms in Pascal, J. Wiley & Sons, New York,
1995.
Mentor: prof.dr. Aleksander Vesel
OLETIČ, M.: Bazna drevesa.
Diplomsko delo, Univerza v Mariboru, Fakulteta za naravoslovje in matematiko,
Oddelek za matematiko in računalništvo, 2010.
IZVLEČEK
Podatkovna struktura je način organizacije podatkov. Področje podatkovnih
struktur obravnava enostavnejše strukture, kot so seznam, sklad, vrsta, tabela ter
zahtevnejše, kot so drevesa, grafi, mnoţice. V diplomskem delu se osredotočimo
na posebno obliko dreves, ki jih imenujemo bazna drevesa.
V prvem poglavju smo za boljše razumevanje nadaljnjih poglavij razloţili pojem
algoritma, pojem podatkovne strukture in pojem drevesa.
Nadaljevali smo z razlago urejanja z radiksom, ki je motivacija za uvedbo baznih
dreves. V tem poglavju razloţimo osnove urejanja z radiksom ter opišemo in
razloţimo dva načina urejanja z radiksom. Oba načina razloţimo še na podlagi
primerov.
Tretje poglavje posvetimo glavni temi našega diplomskega dela, kjer razloţimo
idejo baznih dreves.
Naslednja tri poglavja posvetimo razlagi vsake od podvrst baznih dreves. Na
podlagi teoretične razlage in praktičnega prikaza s primeri razloţimo digitalno
drevo, drevo trie in patricia drevo.
Zadnje poglavje posvetimo razlagi delovanja algoritma, ki smo ga realizirali v
programskem jeziku C++.
Ključne besede: algoritem, drevo, urejanje z radiksom, bazno drevo, digitalno drevo, trie,
patricia.
OLETIČ, M.: Radix trees.
Graduation Thesis, University of Maribor, Faculty of Natural Sciences and
Mathematics, Department of Mathematics and Computer Science, 2010.
ABSTRACT
Data structure is a way of organizing data. The field of data structures treats very
simple structures such as lists, stacks, queues, arrays as well as more pretentious
structures such as trees, graphs, sets. In this diploma thesis we focus on a special
form of trees which are called radix trees.
In the first chapter we explained the idea of algorithm, data structure and tree for a
better understanding of the following chapters.
We continued with the explanation of radix sort which is the motivation for
introducing radix trees. In this chapter we explain the basics of radix sort and
describe two ways of radix sort. Both ways are explained on the bases of
examples.
The third chapter is devoted to the main topic of our diploma thesis, where the
idea of radix trees is explained.
The next three chapters are devoted to the explanations of each sub-sort of radix
trees. On the basis of theoretical explanation and the practical presentation with
examples we explain the digital tree, trie and patricia tree.
The last chapter is devoted to the explanation of the algorithm operation which
was carried out in the C++ programming language.
Key words: algorithm, tree, radix sort, radix tree, digital tree, trie, patricia.
Math. Sci. Class. (2000): 68P05
IX
Kazalo
UVOD .............................................................................................................. 1
1 ALGORITEM, PODATKOVNA STRUKTURA IN DREVO ................................... 3
1.1 Definicija algoritma ..................................................................................... 3
1.2 Zahtevnost algoritma .................................................................................. 6
1.2.1 Časovna zahtevnost ..................................................................................................... 6
1.2.2 Prostorska zahtevnost ................................................................................................. 6
1.3 Podatkovna struktura .................................................................................. 7
1.4 Drevo .......................................................................................................... 7
1.4.1 Dvojiško iskalno drevo ................................................................................................. 8
2 UREJANJE Z RADIKSOM ........................................................................... 10
2.1 Motivacija za urejanje z radiksom .............................................................. 10
2.1.1 Pojem radiksa ............................................................................................................ 11
2.1.2 Ideja urejanja z radiksom .......................................................................................... 12
2.2 Dve obliki urejanja z radiksom ................................................................... 12
2.3 Razlike med MSD in LSD načinom............................................................... 13
2.4 Uporaba MSD metode urejanja z radiksom ................................................ 13
2.5 Uporaba LSD metode urejanja z radiksom .................................................. 14
3 BAZNA DREVESA ..................................................................................... 17
3.1 Osnovna oblika.......................................................................................... 17
3.2 Motivacija za bazna drevesa ...................................................................... 18
3.3 Osnovne operacije ..................................................................................... 18
3.4 Vrste baznih dreves ................................................................................... 23
4 DIGITALNO DREVO .................................................................................. 24
4.1 Vstavljanje podatkov v drevo ..................................................................... 25
4.2 Iskanje podatkov v drevesu ....................................................................... 29
4.3 Brisanje podatkov iz drevesa ..................................................................... 30
5 TRIE ........................................................................................................ 32
5.1 Iskanje podatkov v drevesu ....................................................................... 34
5.2 Vstavljanje podatkov v drevo ..................................................................... 36
5.3 Brisanje podatkov iz drevesa ..................................................................... 38
X
6 PATRICIA ................................................................................................ 41
6.1 Pravila patricia drevesa.............................................................................. 41
6.2 Vstavljanje podatkov v drevo in razvijanje drevesa ..................................... 43
6.3 Iskanje podatkov v drevesu ....................................................................... 52
7 OPIS PROGRAMA .................................................................................... 54
7.1 Vnos podatkov in potek programa ............................................................. 54
7.2 Delovanje programa za primer digitalnega drevesa .................................... 55
ZAKLJUČEK..................................................................................................... 59
LITERATURA .................................................................................................. 61
PRILOGA ........................................................................................................ 62
1
UVOD
Dandanes si ţivljenja brez računalnikov sploh ne moremo več predstavljati.
Uporabljamo jih doma, v šoli, v sluţbi. V nekoliko drugačni obliki so prisotni tudi
v avtomobilih, strojih, naši mobilni telefoni so ţe na nek način nekakšni majhni
računalniki. Če zavrtimo čas za trideset let nazaj, nam lahko naši starši povedo,
kako so takrat profesorji na fakulteti razlagali, da bomo nekoč imeli doma v
stanovanju po dva, tri ali celo štiri računalnike, da bo to nekaj vsakdanjega. In če
pogledamo situacijo danes, lahko tem besedam samo pritrdimo, saj danes skorajda
ne najdemo stanovanja, kjer ne bi imeli vsaj enega računalnika.
Računalniki nam olajšajo marsikatero nalogo ali problem, ki ga vedo rešiti v
bistveno hitrejšem času, v primerjavi s časom, ki bi ga potrebovali ljudje, če bi
hoteli rešiti enak problem ali nalogo. Poleg tega nam omogočajo shranjevanje
velike količine podatkov, urejanje le-teh ter prikaz različnih simulacij, ki jih v
realnem ţivljenju ne moremo predstaviti. S pojavom računalnika se nam je
bistveno olajšalo ţivljenje, lahko rečemo, da skorajda na vseh področjih.
Velikokrat so podatki, ki prihajajo v računalnik, neurejeni, zato potrebujemo
posebne postopke, ki bodo te podatke uredili tako, da bodo le-ti postali urejeni.
Samo urejanje podatkov v računalništvu lahko poteka na različne načine, ki jih
opišemo z algoritmi urejanja. Pomembna komponenta urejanja je hitrost urejanja
podatkov. Najprej so bili le-ti počasnejši in preprostejši. Zaradi potrebe po
hitrejšem urejanju so se razvili različni postopki in pristopi. Posledično je to
privedlo do tega, da poznamo danes številne algoritme urejanja.
Podatki so lahko urejeni tudi v obliki dreves. Drevo je na področju računalništva
pogosto rabljena podatkovna struktura. Elementi so v drevesu urejeni hierarhično
v razmerju »roditelj – otrok« in so med seboj povezani. Ločimo veliko različnih
vrst dreves, na primer dvojiška drevesa, dvojiška iskalna drevesa, bazna drevesa,
B-drevesa, AVL drevesa, itd. V nekaterih vrstah dreves ima roditelj tudi več
otrok, medtem, ko ima otrok vedno le enega roditelja.
2
Dvojiško ali binarno drevo je drevesna podatkovna struktura, kjer ima vsako
vozlišče (roditelj) največ dva otroka. Po navadi se otroka imenujeta levi in desni
otrok. Podatkovna struktura kot je drevo pa ni prisotno samo na področju
računalništva. V našem vsakdanjem ţivljenju lahko na primer s pomočjo
dvojiških dreves predstavimo druţinsko drevo. Tako lahko predstavimo
rodoslovne podatke oziroma sorodstvene relacije neke druţine.
Bazno drevo je posebna vrsta dvojiškega drevesa, ki se v osnovni obliki uporablja
za shranjevanje niza s poljubno dolţino bitov (sestavljajo ga samo ničle in enice).
Ta vrsta drevesa ima svoje različice oziroma podvrste, katere omogočajo
shranjevanje ne samo bitnega niza s poljubno dolţino bitov, ampak tudi druge
podatkovne oblike, kot so črke, besede, ...
Diplomsko delo je razdeljeno na sedem poglavij. Prvo poglavje opredeljuje pojem
algoritma, pojem podatkovne strukture in pojem drevesa. Naslednje poglavje
opiše algoritem urejanja z radiksom. Tretje poglavje predstavi osnovno obliko
baznih dreves. Nadaljnja tri poglavja podajajo razlago vsake od treh podvrst
baznih dreves posebej. V zadnjem poglavju je opisan program za delo z
digitalnim drevesom, ki je realiziran v programskem jeziku C++.
3
1 ALGORITEM, PODATKOVNA STRUKTURA IN
DREVO
Za laţje oziroma boljše razumevanje poglavij, ki sledijo, bomo to poglavje
namenili razlagi definicije algoritma, njegove zahtevnosti ter podatkovne
strukture in razlagi definicije drevesa.
1.1 Definicija algoritma
Vsak dan se srečujemo z različnimi problemi, ki jih tudi rešujemo. Pri tem si
pomagamo z izkušnjami, znanjem, s spretnostjo. Reševanje nekaterih problemov
se nam zdi povsem logično, saj poznamo vse korake, ki nas vodijo do same
rešitve problema. Včasih pa se nam zgodi, da rešitve problema ne poznamo v
naprej. Takrat potrebujemo dodatne informacije, ki nas bodo pripeljale do
končnega cilja. Kot primer vzemimo nakup hiše na hipoteko. Najverjetneje o
poteku takega nakupa nimamo vseh informacij, ne poznamo vseh korakov v
naprej, ki bi nas pripeljali do cilja, torej do nakupa hiše, v primerjavi z nakupom v
trgovini, kjer točno vemo, kako poteka postopek nakupovanja. Nekateri problemi
ne zahtevajo samo naše iznajdljivost in ustvarjalnost in zato med te vrste
problemov prištevamo tudi razvoj algoritmov. Algoritmično razmišljanje človeku
ni prirojeno. V to se lahko prepričamo ob primerih iz našega ţivljenja [3].
Čeprav se nam zdi, da nam je jasno kaj je treba storiti (razumemo vprašanje
KAJ?), se nam pri izpeljavi dejstva kako to storiti (vprašanje KAKO?) lahko kaj
hitro zaplete, tako da lahko pridemo na koncu čisto nekam drugam, samo k
zastavljenemu cilju ne. Da bi bili pri reševanju problemov uspešni, moramo
upoštevati naslednje principe [4]:
razumevanje problema: problem smo ţe na pol rešili, ko vemo, kateri
problem je potrebno rešiti. Najprej je pomembno razumevanje problema,
potem sledi nedvoumna definicija problema, opis dejstev, pravil in ciljev.
4
abstrakcija problema: opis problema je treba abstrahirati, tako da se
obdrţijo samo pomembni podatki; nepomembne podatke je treba
zanemariti. Pri abstrakciji identificiramo pomembne objekte, jih
imenujemo, določimo relacije med njimi in definiramo operacije na teh
objektih.
izbira notacije: izbira ustrezne notacije ključno vpliva na hitrost in
uspešnost reševanja problemov. Večji del programiranja je spreminjanje
notacije, ki jo razume človek, v notacijo, ki jo razume stroj. Notacija je
simbolična predstavitev, ki modelira skupne značilnosti razreda objektov
ali situacije in moţne operacije na simbolih. Uporabljamo slikovne ali
besedne simbole. Katere izberemo je povsem odvisno od subjektivne
izbire. Nekateri si laţje vizualizirajo problem, drugim pa je bliţji besedni
opis.
razbitje problema na podprobleme: pri reševanju kompleksnih problemov
je pomembna razgradnja problema na manjše zaokroţene podprobleme.
Pomembno je, da poskušamo kompleksen problem razgraditi na
preprostejše podprobleme, ki jih lahko rešujemo neodvisno. Problem
navadno razgrajujemo od zgoraj navzdol, lahko pa izberemo tudi obratno
strategijo, kjer iz rešitev manjših problemov sestavljamo rešitve bolj
zapletenih problemov.
Slika 1: Snovanje algoritmov
5
podobnost med problemi: pogosto si lahko pri reševanju problemov
pomagamo z rešitvami podobnih problemov. S transformacijo lahko
problem poenostavimo, posplošimo ali samo preoblikujemo v izomorfni
problem. Z rešitvijo poenostavljenega problema lahko dobimo idejo za
rešitev bolj zapletenega problema.
Beseda algoritem je izpeljana iz imena perzijskega matematika Abu Ja`far
Mohamed ibn Musa al Khowarizmi, in sicer na naslednji način [3]:
al-Khowarizmi → algorism → algorithm → algoritem.
Za podajo natančne definicije algoritma bi se morali nasloniti na formalni model
računalnika, kateremu je seveda algoritem namenjen. V definicijo bi mogli
vključiti pojme, kot so: nabor podatkov, osnovne operacije, organizacijo dela, …
Za nas bodo primerni algoritmi, ki jih izvajamo na računalniku, zato lahko
podamo naslednjo definicijo algoritma [4].
Algoritem je končno zaporedje natančno določenih ukazov, ki opravijo neko
nalogo v končnem številu korakov. Algoritem sprejme vhodne podatke in vrne
rezultat. Zanj velja, da ima podatke, vrne rezultate, je natančno določen, saj mora
vsak ukaz nedvoumno povedati, kaj storiti in da se vedno konča.
Ţe prej omenjeno vprašanje KAJ je treba storiti, moramo razgraditi na manjše,
laţje probleme, ki nam posledično povedo KAKO to storiti. Zato je v končnem
smislu algoritem nekakšno zaporedje preprostih ukazov, kot so na primer: seštej,
odštej, zmnoţi, pogoj, v katerem določamo ali je neka vrednost večja, manjša ali
enaka 0, itd.
Algoritem lahko zapisujemo [4]:
z diagrami poteka – pomeni grafični prikazni potek,
s psevdokodo – jezik za zapis algoritmov (lahko smo manj natančni),
v programskih jezikih – zahtevano poznavanje podrobnosti programskega
jezika, omejeni smo z ukazi programskega jezika.
6
1.2 Zahtevnost algoritma
Kot smo ţe prej omenili v definiciji besede algoritem, je algoritem rešitev nekega
problema. Računalniku ga ne pustimo v slepo izvajanje, ampak ga analiziramo
koliko časa bo tekel in koliko prostora bo zasedal v računalniškem pomnilniku.
Pravimo, da analiziramo njegovo časovno in prostorsko zahtevnost. V splošnem z
zahtevnostjo imenujemo količino računalniški virov in sredstev, ki jih
potrebujemo za rešitev problema po izbranem postopku.
1.2.1 Časovna zahtevnost
Poleg preverjanja pravilnosti algoritma je najpomembnejši del analize algoritma
določitev njegove časovne zahtevnosti. Časovna zahtevnost je odvisna od
velikosti vhodnih podatkov. Na zahtevnost lahko vplivajo različne vrednosti
vhodnih podatkov, na primer število podatkov, velikost posameznega podatka,
vrstni red podatkov, … [4]. Merimo jo v osnovnih aritmetično-logičnih operacijah
(seštevanje, odštevanje, mnoţenje, deljenje, primerjave/prireditve). Časovno
zahtevnost označimo s T(n). Oznaka T(n) je funkcija, ki podaja hitrost naraščanja
zahtevnosti s številom podatkov.
Časovno zahtevnost lahko dobimo na dva načina:
analiziramo algoritem in preštejemo, koliko bo opravil ali
preprosto izmerimo, koliko časa potrebuje glede na različne vhodne
podatke.
1.2.2 Prostorska zahtevnost
Prostorska zahtevnost je količina pomnilnika, potrebnega za izvajanje algoritma.
Merimo jo v enotah primarnega ali sekundarnega pomnilnika (zlogi, bloki,
besede).
Glede na vhodne podatke poiščemo, koliko časa teče algoritem za najneugodnejše
in najugodnejše podatke. Dobljeni vrednosti imenujemo časovna zahtevnost v
najslabšem in najboljšem primeru. Pri ocenjevanju zahtevnosti nas večinoma ne
bo zanimalo, koliko operacij ali prostora potrebujemo, temveč nas bo zanimal le
7
red velikosti hitrosti rasti zahtevnosti algoritma in ne natančna vrednost. Glede na
red velikosti hitrosti rasti zahtevnosti algoritma v odvisnosti od števila podatkov
delimo probleme na: lahke in teţke. Med lahke algoritme spadajo algoritmi, ki se
izvršijo v polinomskem času, medtem ko med teţke prištevamo algoritme, ki
zahtevajo eksponentno rešitev [3].
1.3 Podatkovna struktura
Študija algoritmov in podatkovnih struktur sta med seboj tesno povezana, saj
pravzaprav govorita o isti stvari iz dveh zornih kotov [3]. Vsaka podatkovna
struktura ima dva vidika – matematičnega in računalniškega. Ko gledamo na
podatkovno strukture iz matematičnega vidika, nas zanimajo predvsem njene
matematične lastnosti. V računalništvu pa se ukvarjamo z vprašanjem, kako
podatkovno strukturo predstavimo v računalniku in kako učinkovito opravljamo
osnovne operacije na podatkovni strukturi.
Podatkovna struktura je način organizacije podatkov. V vsakdanjem ţivljenju
srečamo podatkovne strukture kot so seznam, sklad, vrsta, tabela itd. V
računalništvu poznamo še nekatere druge podatkovne strukture, kot so drevesa,
grafi, mnoţice.
Podatkovna struktura določa:
kako so podatki predstavljeni,
kakšne so osnovne operacije za delo s podatkovno strukturo.
Katero podatkovno strukturo izberemo, je odvisno od tega, kaj bi radi s podatki
počeli.
1.4 Drevo
Obstaja nekaj različnih definicij pojma drevesa. V teoriji grafov je drevo
definirano kot povezan graf brez ciklov. Osnovni element v drevesu je vozlišče.
8
Vsako vozlišče ima lahko nič ali več otrok, vendar kvečjemu enega roditelja.
Koren je vrhnje vozlišče, edino vozlišče v drevesu brez roditelja.
Oglejmo si nekaj definicij pojmov, ki se nanašajo na podatkovno strukturo drevo:
koren drevesa: samo eno vozlišče v nepraznem drevesu je brez roditelja in
mu pravimo koren drevesa.
vozlišče: osnovni element drevesa, ki vsebuje podatke in informacije o iz
njega izhajajočih poddrevesih. Vozlišče ima lahko več potomcev, prednika
pa ima samo enega.
list: vozlišče brez otrok.
notranje vozlišče: vozlišče, ki ima vsaj enega otroka.
poddrevo: poddrevo danega vozlišča je drevo, katerega koren je otrok
danega vozlišča. Otrokom istega roditelja pravimo sorojenci.
stopnja vozlišča: število otrok danega vozlišča.
stopnja drevesa: največje število otrok nekega notranjega vozlišča.
prazno drevo: drevo brez vozlišč.
dvojiško drevo: vsako vozlišče ima največ dva otroka.
1.4.1 Dvojiško iskalno drevo
Dvojiško iskalno drevo je dvojiško drevo, za katerega v vsakem vozlišču velja, da
so vsi ključi v levem poddrevesu manjši od korena in v desnem poddrevesu večji
od korena.
9
Slika 2: Primer drevesa
Pri dvojiških iskalnih drevesih poznamo tri osnovne operacije:
vstavljanje v drevo: ţelen element vstavljamo v drevo. Če koren drevesa še
ne obstaja, potem prvi element, ki ga vstavljamo postane koren drevesa.
Drugače poteka vstavljanje v drevo po pravilih, ki ga določa le-ta.
iskanje po drevesu: iščemo nek element v drevesu, kjer kot prvo preverimo
če obstaja koren drevesa. Če le-ta obstaja, nadaljujemo iskanje po drevesu.
brisanje iz drevesa: pri brisanju imamo več moţnosti glede na to, ali je
vozlišče, ki ga ţelimo izbrisati, list ali ima vozlišče enega samega otroka
ali pa ima vozlišče dva otroka.
10
2 UREJANJE Z RADIKSOM
V vsakdanjem ţivljenju se marsikdaj srečamo s problemom, ko ţelimo nekaj
zloţiti, urejati, na primer po velikosti, barvi, po abecedi. Kot vemo iz izkušenj,
med urejenimi predmeti laţje najdemo ţelenega. Podoben pomen ima urejanje
tudi v svetu računalništva. Zelo pomemben je zato študij algoritmov za urejanje.
Ločimo primerjalno urejanje, kjer poteka pregledovanje elementov s
primerjalnimi operatorji, ponavadi je to operator manjše ali enako (≤). Ta vrsta
urejanja zajema naslednje znane algoritme za urejanje: urejanje z vstavljanjem,
urejanje z mehurčki, hitro urejanje, urejanje z zlivanjem, urejanje s kopico.
Obstaja še druga vrsta urejanja in sicer neprimerjalno urejanje. To uporablja druge
metode za razvrščanje podatkov in ne uporablja primerjalnih operatorjev. V to
vrsto prištevamo: urejanje z radiksom (proučuje posamezne bite v ključu),
urejanje s štetjem (šteje pojavitve vrednosti elementov), urejanje s koši/sektorsko
urejanje (preučuje bite ključa). Algoritem urejanja z radiksom bomo v
nadaljevanju poglavja podrobneje opisali in prikazali na primeru, saj bo sluţil kot
motivacija za bazna drevesa.
2.1 Motivacija za urejanje z radiksom
Nekoč so bili računalniški programi napisani v programskem jeziku Fortran,
katere so vnašali v računalnik s pomočjo luknjanih kartic. Na vsaki kartici je bila
v stolpcih 1 do 72 zapisana koda programa, stolpci od 73 do 80 so pomenili
številko kartice. Če so kartice po nesreči kdaj padle na tla, je nastal velik problem,
saj je bilo za en program potrebnih tudi 2000 kartic. Problem so lahko rešili tako,
da so pomešane kartice vstavili v napravo, ki jih je razvrstila po vrstnem redu [6].
Naprava za urejanje kartic je bila velikosti treh ali štirih velikih omar. Imela je
vhod za kartice in deset izhodnih zabojev, ki so bili oštevilčeni od 0 do 9. Prebrala
je vsako kartico in jo postavila v zaboj, glede na številko v določenem stolpcu, na
primer stolpcu 80. Tako dobimo deset kupov kartic, en kup v vsakem zaboju.
11
Kartice so bile urejene najprej po najmanj pomembni cifri (stolpec 80), pozneje
po najpomembnejši (stolpec 73) [6].
Številne aplikacije urejanja, ki uporabljajo ključ za določitev vrstnega reda
zapisov, so lahko kar zahtevne. Kot primer lahko podamo kompleksnost ključa v
telefonskem imeniku ali v knjiţničnem katalogu. Obdelava celega ključa na
vsakem koraku je pogosto nepotrebna. Pri iskanju v telefonskem imeniku
preverjamo samo nekaj prvih črk in ne celega imena, torej samo del ključa.
Urejanje podatkov, kjer je ključ, ki definira urejenost podatkov, zapleten, lahko
nadomesti učinkovita metoda urejanja po delih, saj uporabljamo to metodo takrat,
kadar je ključ moţno razbijati na dele, ki so fiksne dolţine (bite, znake, cifre).
Binarna števila so zaporedja bitov, nizi so zaporedja znakov, decimalna števila so
zaporedja cifer. Povsem drugačen pogled na razvrščanje nam da razvrščanje z
delčki ključa, v primerjavi z razvrščanjem s celim ključem. Metode urejanja, ki za
urejanje uporabljajo samo del ključa naenkrat imenujemo urejanje z radiksom.
Razlago lahko podkrepimo še s primerom s področja pošte. Sortirni stroj na pošti
obdeluje kupe paketov, ki so označeni s 4-mestno poštno številko. Najprej jih
razporedi na deset kupov: prvi ima števila, ki se začnejo z 0, naslednji ima števila,
ki se začnejo z 1 in tako naprej. Kupi se lahko obdelujejo posamezno, z uporabo
metode naslednje cifre ali pa s kakšno drugo metodo. Če bi izbirali pakete iz
kupov v zaporedju od 0 do 9 tako kot so bili obdelani, bi dobili urejeno
razporeditev. Tak postopek predstavlja urejanje z radiksom z R=10 in je glavna
metoda v aplikacijah, kjer so ključi sestavljeni iz 5 do 10 mestnih števil kot so
poštne številke, telefonske številke oz. številke zdravstvenega zavarovanja [5].
2.1.1 Pojem radiksa
V grobem lahko rečemo, da je radiks števka oziroma poloţaj števke ključa
predstavljenega v izbrani bazi. V desetiškem sistemu je radiks le števka
desetiškega števila. Tako ima število 42 dve števki ali dva radiksa, to sta 4 in 2. V
šestnajstiškem sistemu ima radiks 8 bitov.
12
2.1.2 Ideja urejanja z radiksom
Algoritmi urejanja z radiksom so zasnovani na abstraktnih operacijah "izvleci i-to
cifro iz ključa". Zato lahko pravimo, da je urejanje z radiksom urejanje z več
prehodi oz. urejanji. Število urejanj je enako številu radiksov vhodnega števila.
V algoritmih urejanja z radiksom so deli ključa določene velikosti. Če so v ključu
številke, potem poteka urejanje po načelu števka za števko, če pa so v ključu črke,
ki jih ţelimo razvrstiti po abecedi, potem uporabimo načelo črka za črko.
Urejanje z radiksom zahteva čas O(d (n + k)) = O(dn + dk), pri čemer je n velikost
ali število elementov, ki jih je treba urediti. Spremenljivka d predstavlja število
števk elementa oziroma kar dolţino ključa, kar pa posledično predstavlja število
urejanj. Vsaka števka je reda od 1 do k [6].
2.2 Dve obliki urejanja z radiksom
Obstajata dva bistveno različna osnovna pristopa k urejanju z radiksom, in sicer:
Prvi pristop vključuje algoritem, ki pregleduje števke v ključu od leve
strani proti desni strani, saj uporablja najbolj pomembno števko najprej
oziroma najprej obdela najpomembnejše števke. Te vrste algoritmov
imenujemo urejanje z najbolj pomembno števko ali MSD urejanje (most-
significant-digit (MSD) radix sort). MSD metoda urejanja je ugodna zato,
ker pregleduje minimalno količino informacij, potrebnih za uspešno
opravljeno razvrščanje oziroma urejanje. MSD urejanje posplošuje hitro
urejanje, ker je delitev »problema« razvrščena glede na najpomembnejše
števke ključa.
Druga vrsta metod urejanja z radiksom je drugačna. Ta metoda pregleduje
števke v ključu od desne strani proti levi strani, torej od najmanj
pomembne števke najprej. Ta metoda je splošno znana pod imenom
urejanje z najmanj pomembno števko ali LSD urejanje (least-significant-
digit (LSD) radix sort). LSD metoda deluje ravno obratno kot metoda
MSD.
13
2.3 Razlike med MSD in LSD načinom
Če poznamo pri metodi MSD najmanjše število znakov, potrebnih za
razlikovanje vseh znakov, potem lahko urejamo ta števila po poloţaju. To
bi na podlagi primera lahko razloţili takole: ko so besede dolge in jih
lahko razlikujemo samo po prvih nekaj znakih, potem jih lahko
razvrščamo samo po na primer prvih treh znakih ključa. To pomeni, da
nam ni potrebno pregledovati cele dolţine ključa.
LSD pristop zahteva polnjenje oziroma dopolnjevanje kratkega ključa, če
je dolţina ključa različna. S tem bo zagotovljeno, da bodo vse števke
pregledane in da bo zaporedje urejeno. Kot primer lahko podamo zapis
števila v binarnem načinu: 3 → 112 in 12 → 11002. To pomeni, da bosta
ključu 3 dodane od spredaj še dve ničli, tako da bomo imeli naslednja
ključa: 0011 in 1100.
MSD metoda urejanje zahteva veliko več pomnilnika za razvrščanje
elementov, zato je LSD urejanje primernejša metoda.
MSD rekurzivno urejanje lahko uporabimo za vzporedno urejanje, kar
pomeni, da lahko vsak podseznam uredi neodvisno od ostalih.
2.4 Uporaba MSD metode urejanja z radiksom
Predpostavimo, da imamo ključa oblike (j, k), kjer j pomeni eno od črk med A in
F in k, ki predstavlja števko. Ključa lahko razvrstimo v šest zabojev. Enega za
vsako od črk. Po tem vsak seznam razvrstimo še po števkah. Kot primer podajmo
naslednje zaporedje [1]:
A6, C9, F5, F4, B9, D2, E9, D3, C3, B3, A5, A9, E1, F2, D4, A7, D1
14
1. korak: razvrstitev po črki. Po urejanju dobimo naslednje podsezname:
A6 A5 A9 A7
B9 B3
C9 C3
D2 D3 D4 D1
E9 E1
F5 F4 F2
2. korak: sedaj pa urejamo še znotraj vsakega podseznama še po njihovem
drugem parametru, torej po števki.
A5 A6 A7 A9
B3 B9
C3 C9
D1 D2 D3 D4
E1 E9
F2 F4 F5
3. korak: urejeno zaporedje:
A5 A6 A7 A9 B3 B9 C3 C9 D1 D2 D3 D4 E1 E9 F2 F4 F5
Urejeno polje dobimo z enostavno spojitvijo dobljeni podseznamov. Tak način je
splošno neroden za urejanje, zato je bolje uporabiti kaj primernejšega. Mi smo
urejali od skrajno levega dela ključa k skrajno desnemu. Če bi hoteli uporabiti
stabilnejše razvrščanje, potem bi morali uporabiti urejanje od skrajno desne proti
skrajno levi strani. S stabilno delitveno metodo lahko prav tako urejamo od
najmanj pomembnega bita proti najbolj pomembnemu bitu. Takšen primer bomo
razloţili v naslednjem podpoglavju.
2.5 Uporaba LSD metode urejanja z radiksom
LSD metoda urejanja pregleda niz od desne strani proti levi. Spodnji primer
prikazuje, kako lahko samo s šestimi prehodi čez datoteko opravimo urejanje
15
nizov s šestimi znaki. I-ti stolpec dobimo iz (i-1)-tega stolpca z izvlečenjem vseh
ključev z 0 na i-tem mestu in vračanjem nad ključe z 1 na i-tem mestu. Poglejmo
si naš primer LSD urejanja. Naš primer ima največji radiks 6, kar posledično
pomeni, da bomo izvedli šest urejanj.
I. 1. korak: najprej uredimo zaporedje glede na najmanj pomembno cifro.
II. 2. – 5. korak: nato uredimo zaporedje glede na drugo najmanj pomembno cifro.
Tako sledimo korakom urejanja vse do najpomembnejše cifre.
1 1 0 1 1 0
0 0 1 1 0 0
0 0 1 0 0 1
0 1 0 1 1 1
1 0 1 1 1 1
0 1 1 1 0 0
1 1 0 1 1 0
0 0 1 1 0 0
0 1 1 1 0 0
0 0 1 0 0 1
0 1 0 1 1 1
1 0 1 1 1 1
0 0 1 1 0 0
0 1 1 1 0 0
0 0 1 0 0 1
1 1 0 1 1 0
0 1 0 1 1 1
1 0 1 1 1 1
16
III. 6. korak: zaporedje uredimo še glede na najbolj pomembno cifro.
IV. 7. korak: urejeno zaporedje.
9 →
12 →
23 →
28 →
47 →
54 →
0 0 1 0 0 1
0 0 1 1 0 0
0 1 1 1 0 0
1 1 0 1 1 0
0 1 0 1 1 1
1 0 1 1 1 1
1 1 0 1 1 0
0 1 0 1 1 1
0 0 1 0 0 1
0 0 1 1 0 0
0 1 1 1 0 0
1 0 1 1 1 1
1 1 0 1 1 0
0 1 0 1 1 1
0 0 1 0 0 1
0 0 1 1 0 0
0 1 1 1 0 0
1 0 1 1 1 1
0 0 1 0 0 1
0 0 1 1 0 0
0 1 0 1 1 1
0 1 1 1 0 0
1 0 1 1 1 1
1 1 0 1 1 0
17
3 BAZNA DREVESA
3.1 Osnovna oblika
Bazno drevo je posebna vrsta dvojiškega drevesa, ki se v osnovni obliki uporablja
za shranjevanje nizov s poljubno dolţino bitov. Nize torej sestavljajo samo ničle
in enice. Vsako vozlišče ima dve razvejitvi, ki ustrezata dvojiški števki (bitu).
Nepisano pravilo pravi, da je leva razvejitev oštevilčena z 0, desna pa z 1. Vsaka
pot neničelne dolţine skozi bazno drevo se začne pri korenu drevesa, zato je ta
posledično sestavljena iz edinstvenega zaporedja ničel in enic [7].
Poleg ţe zgoraj omenjenih lastnosti baznega drevesa moramo omeniti še nekaj
značilnosti:
vsi podatki so shranjeni v listih drevesa,
imamo dve vrsti vozlišč: notranja in zunanja vozlišča. Notranje vozlišče
drevesa vsebuje samo povezave do vozlišč, medtem ko zunanje vozlišče
vsebuje ključe.
vejitve do posameznih listov v drevesu so dolge toliko, kolikor je
potrebnih korakov, da se dva ključa razlikujeta v bitni vrednosti,
oblika drevesa ni odvisna od vrstnega reda vstavljenih podatkov.
18
Slika 3: Primer baznega drevesa
3.2 Motivacija za bazna drevesa
Pri določitvah, kako nastane drevo in kako se išče po drevesu, se velikokrat
uporablja celoten ključ. Obstajajo primeri, kjer je pa mogoče uporabiti le del
ključa. To idejo smo uporabili ţe pri poglavju urejanja z radiksom. Takšen način
lahko uporabimo tudi pri tvorjenju baznih dreves. Ta podatkovna struktura, z
nekaj izjemami, ne more obravnavati podvojenih ključev, zato bomo v naslednjih
poglavjih privzeli, da so vsi ključi med seboj vedno različni.
3.3 Osnovne operacije
Vstavljanje
Postopek vstavljanja začnemo v korenu drevesa. Najprej preverimo, če ta ţe
obstaja v drevesu. Če koren ţe obstaja, preverimo prvi bit ključa, ki ga ţelimo
vstaviti, saj le-ta pove, v katero poddrevo se bomo usmerili. Tako pregledujemo
bitni niz ključa (bit za bitom). Glede na bitni niz se pomikamo po drevesu, dokler
ne doseţemo zunanje vozlišče. Če je zunanje vozlišče prazno, enostavno shranimo
ključ na to mesto v drevesu. Če list vozlišča ţe vsebuje nek ključ, potem ta
postane notranje vozlišče. Na tem notranjem vozlišču ustvarimo novo poddrevo,
19
katerega vejitev je odvisna od tega, kako se ţe prej vstavljeni ključ in ključ, ki ga
ţelimo vstaviti, razlikujeta v bitnem nizu. Ustvarimo novo notranje vozlišče, na
katerega poveţemo ţe prej vstavljeni ključ in ključ, ki smo ga ţeleli vstaviti.
Ţe vstavljeni elementi v bazno drevo:
Slika 4: Vstavljeni elementi v bazno drevo
V bazno drevo, ki ga kaţe slika 4 ţelimo vstaviti ključ 59, ki ima dvojiški zapis
1110112. Vrednost bita 0 ključa 59 je 1, kar pomeni, da se bomo pomaknili v
desno poddrevo. Nato preverimo vrednost naslednjega bita, ki je spet 1 (spet se
pomaknemo desno). Tu naletimo na list z vrednostjo 60. Ker smo naleteli na list,
moramo na mestu lista ustvariti novo notranje vozlišče. Koliko novih notranjih
številka 15 6 31 60 2 46 20
dvojiški
zapis 001111 000110 011111 111100 000010 101110 010100
Tabela 1: Elementi v dvojiškem zapisu
20
vozlišč bomo ustvarili, je odvisno od primerjave bitnega niza ključa 60 in 59. Ker
sta vrednosti bita 1 in bita 2 teh dveh ključev enaka, pomeni, da bomo ustvarili še
dve notranji vozlišči. V bitu 3 se vrednosti razlikujeta, zato bomo na drugo
vstavljeno notranje vozlišče povezali ţe prej vstavljeno vozlišče 60 in novi ključ
59. Ključ 59 bomo vstavili na levo stran notranjega vozlišča (vrednost bita 3 je
enaka 0). Vozlišče 60 ima v tem bitu vrednost 1, zato ga vstavimo na desno stran
(glej sliko 5).
Slika 5: Vstavitev elementa v bazno drevo
Iskanje
Iskanje pričnemo v korenu drevesa. Če ţe le-ta obstaja v drevesu, preverimo prvi
bit ključa, za katerega ţelimo preveriti ali obstaja v drevesu. Prvi bit ključa nam
določi, v katerem poddrevesu bomo nadaljevali iskanje. Če ima bit 0 vrednost nič,
bomo šli v levo poddrevo in če ima bit 0 vrednost 1, bomo iskali v desnem
poddrevesu. Pri premikanju po drevesu ne primerjamo nobenih vrednosti med
seboj, saj se po drevesu premikamo glede na bitni niz, ki ga ima iskani ključ.
Postopek iskanja poteka tako dolgo, dokler ne pridemo do zunanjega vozlišča
oziroma lista. Nato preverimo, če je iskana vrednost ključa enaka vrednosti v
listu.
21
Na primeru baznega drevesa, ki ga kaţe slika 3, bomo prikazali postopek iskanja.
Poiskati ţelimo vozlišče 6. Začnemo pri korenu, kjer najprej preverimo vrednost
bita 0, ki je 0. To pomeni, da se usmerimo v levo poddrevo. Vrednost bita 1 in
bita 2 je prav tako 0, zato se bomo še dvakrat pomaknili v levo. Bit 3 ima vrednost
1, zato se pomaknemo v desno stran vozlišča. Ker naletimo na list, preverimo, če
je vrednost v listu enaka vrednosti, ki jo iščemo. Če je ta enaka, pomeni da smo
našli ţeleno vozlišče.
Slika 6: Iskanje elementa po baznem drevesu
Brisanje
Pri postopku brisanja izbrišemo list vozlišča. Postopek brisanja pričnemo s
premikanjem po drevesu glede na bitni niz iskanega vozlišča. Ko najdemo ţelen
list, ki ga ţelimo izbrisati, lahko pričnemo z brisanjem. Če ima notranje vozlišče
razen lista, ki ga ţelimo izbrisati še en drug list, izbrišemo najprej ţelen list. Nato
moramo odstraniti še nepotrebna notranja vozlišča, ki so nastala ob vstavitvi
enega od obeh ključev.
Spet vzemimo primer baznega drevesa iz slike 3. Izbrisati ţelimo list z vrednostjo
59. V drevesu poiščemo list in pričnemo s postopkom brisanja. Po izbrisu
ţelenega lista, izbrišemo še nepotrebna notranja vozlišča.
22
Slika 7: Brisanje elementa iz baznega drevesa
Po izbrisu dobimo naslednjo obliko drevesa, kot kaţe spodnja slika.
Slika 8: Bazno drevo po izbrisu elementa
23
3.4 Vrste baznih dreves
Najpomembnejše različice baznih dreves so:
digitalno drevo,
trie,
patricia drevo.
V nadaljnjih treh poglavjih bomo podrobneje razloţili vsako od podvrst baznih
dreves.
24
4 DIGITALNO DREVO
Kot prvo podvrsto baznih dreves bomo podrobneje obravnavali digitalno drevo.
Ta vrsta drevesa deluje podobno kot dvojiško iskalno drevo, z razliko pri
razvejevanju, ki temelji na bitih iskanega ključa. Pot od korena drevesa do
elementa se določi z dvojiško predstavitvijo ključa. Za laţje razumevanje
pričnimo s prikazom primera na podlagi naslednjega algoritma, ki vstavi novo
vozlišče v digitalno drevo.
I. vrednost n postavimo na 0 (n – indeks bita ključa);
II. če je koren prazen (ne obstaja), vstavimo novo vozlišče v koren;
III. sicer, dokler je n manjši ali enak številu bitov v ključu:
a. testiraj n-ti bit ključa:
i. če ima bit vrednost 0, vstavi vozlišče v levo poddrevo.
ii. če ima bit vrednost 1, vstavi vozlišče v desno poddrevo.
b. povečaj n;
Kot primer vzemimo naslednje zaporedje ključev, ki jih bomo vstavljali v drevo.
Niz ključev je naslednji: 12, 5, 10, 60, 25, 31, 70, 43.
V tej strukturi bitni vzorec ključa določa, kam bo šel zapis v drevesu. Najprej
moramo razčistit nekaj dejstev. Prvo se glasi, da ne obstaja dogovor, kako
številčiti bite ključa. Nekateri jih številčijo od leve strani proti desni, drugi jih
številčijo od desne proti levi. Nekateri jih začnejo številčiti z 0, drugi pa z 1, torej
to pomeni, da je kot prvi bit razumljeni bit 0 ali da je kot prvi bit razumljeni bit 1.
Mi bomo od sedaj uporabljali naslednje pravilo. Bite ključa bomo številčili od
desne strani proti levi, začenši z bitom 0. Če izberemo moţnost številčenja od leve
proti desni strani, potem moramo poznati najdaljši ključ, saj moramo poznati
25
skrajno levo pozicijo bita. V večini programskih jezikov je laţje preveriti skrajno
desni bit, saj se lahko pomikamo, spremenimo poloţaj ključa za eno mesto v
desno, namesto, da hranimo nek bitni števec n.
4.1 Vstavljanje podatkov v drevo
Za laţje razumevanje si najprej poglejmo primer. Kot smo ţe prej zapisali, imamo
naslednji niz ključev: 12, 5, 10, 60, 25, 31, 70, 43. Ključe pretvorimo v dvojiško
obliko kot kaţe spodnja tabela.
S klicem programa razporedimo in napolnimo novo vozlišče. V našem primeru
bomo začeli s prvim številom, ki je 12. Ker nismo vstavili še nobenega ključa je
naš koren drevesa prazen oziroma še ne obstaja, zato dobi vrednost 12.
Slika 9: Vstavitev prvega elementa v digitalno drevo
Naslednji ključ je 5, ki ga dvojiško zapišemo kot zaporedje ničel in enic: 101.
Koren drevesa ţe obstaja in 0-ti bit ključa 5 je enak 1, zato gre ključ 5 v desno
poddrevo.
številka 12 5 10 60 25 31 70 43
dvojiški
zapis 1100 101 1010 111100 11001 11111 1000110 101011
Tabela 2: Podatki v dvojiški obliki
26
Slika 10: Vstavitev elementa 5
Tretji ključ je 10, ki ga dvojiško zapišemo 1010. Koren drevesa ţe obstaja, zato
preverimo bit 0, ki ima vrednost 0, zato gre 10 v levo poddrevo.
Slika 11: Vstavitev elementa 10
Naslednji ključ je 60=1111002. Ker koren drevesa ţe obstaja in je bit 0 enak 0, gre
60 v levo poddrevo. Vendar koren levega poddrevesa ni prazen, saj ţe vsebuje
ključ 10, zato moramo preveriti bit 1 ključa 60. Bit 1 je 0, zato gre 60 na levo
stran poddrevesa s korenom 10.
27
Slika 12: Vstavitev elementa 60
Peti ključ je 25=110012. Bit 0 je 1, zato gre ta ključ v desno poddrevo. V desnem
poddrevesu ţe obstaja ključ 5, zato pogledamo katero vrednost ima bit 1. Njegova
vrednost je 0, zato gre 25 na levo stran ključa 5.
Slika 13: Vstavitev elementa 25
Sledi ključ 31=111112. Bit 0 je 1, zato gre ključ v desno poddrevo, ker pa v tem ţe
obstajata 5 moramo preveriti bit 1, ki je 1 in ker desna stran vozlišča še ni
zasedena, gre 31 na desno stran vozlišča z vrednostjo 5.
28
Naslednji ključ je 70=10001102, ki ima bit 0 z vrednostjo 0, zato se premaknemo
v levo poddrevo, v katerem ţe obstaja 10. Preverimo bit 1, ki je 1. Ker je desna
stran še prosta, gre 70 na desno stran ključa 10.
Slika 14: Vstavitev elementa 70
Zadnji ključ, ki sledi je 43=1010112. Bit 0 je 1, zato gre v desno poddrevo. Tam
ţe obstaja 5. Preverimo bit 1, ki je 1 in ker na tem mestu ţe obstaja ključ 31
preverimo bit 2, ki je 0 in ker vozlišče z vrednostjo 31 še nima otrok, gre ta lahko
zaradi bita 2, ki ima vrednost 0 na levo stran.
29
Slika 15: Vstavitev elementa 43
Prej smo določili, da bomo številčenje bitov izvajali od desne strani proti levi,
začenši z bitom 0. Ob tej predpostavki je zapisana koda za vstavljanje ključev v
drevo navedena v prilogi diplomskega dela.
4.2 Iskanje podatkov v drevesu
Procedura iskanja je skoraj tako preprosta kot procedura vstavljanja. Spremenimo
običajno iskalno funkcijo, tako da se uporablja bitni model za izbiro, v katerem
poddrevesu bomo iskanje izvajali. Od tedaj ga bomo primerjali s ključem (da se
ugotovi ali ga vsebuje trenutni koren) in ga premaknili v vrsto za dostop do drugih
bitov. Algoritem ima naslednjo obliko:
I. če koren še ne obstaja, funkcija vrne false (element ni bil najden);
II. če je iskani ključ enak ključu v korenu, potem funkcija vrne true (element
je najden);
III. če iskani ključ ni enak ključu v korenu, preverjamo bit za bitom:
i. če ima preverjeni bit vrednost 0, nadaljujemo iskanje v levem
poddrevesu.
30
ii. drugače nadaljujemo iskanje v desnem poddrevesu.
IV. če je bil iskani ključ najden, funkcija vrne true, drugače pa false;
Pripomniti moramo, da obstaja omejitev glede višine drevesa. Ni pomembno,
kako se je razvijalo do tedaj, če je najdaljši shranjeni ključ dolg b bitov. Dolţina
poti do tega ključa ne more biti daljša kot b-1. Na ta način je zagotovljena
košatost drevesa, če je shranjenih veliko ključev. Še več, celo izrojeno drevo ne
bo zelo visoko. Od tod lahko zaključimo, da so stroški iskanja po digitalnem
drevesu, ki vsebuje n elementov, v najslabšem primeru b-1.
Če so ključi, ki jih vstavljamo v drevo, nizi znakov, potem bo premikanje in
testiranje bitov bolj zapleteno, saj moramo takrat vzeti v obzir število bitov za
znak (sedem ali osem) in moramo nekako urediti premik za celotni niz, ko smo
prišli na konec znaka. V naslednjem poglavju si bomo ogledali še drugačno
izvedbo obdelave znakovnih nizov.
4.3 Brisanje podatkov iz drevesa
Pri digitalnem drevesu je postopek brisanja relativno enostaven, saj lahko
izbrisano vozlišče nadomestimo s katerim koli listom v poddrevesu, katerega
koren je izbrisano vozlišče. Tako list enostavno zamenjamo z vozliščem, ki ga
ţelimo izbrisati. Kazalec, ki je prej kazal na list, nastavimo na NULL.
Vzemimo prikazan primer digitalnega drevesa, ki ga kaţe slika 16. Izbrisati
ţelimo vozlišče z vrednostjo 5. Najprej moramo poiskati ţeleno vozlišče v
drevesu. Ko ga najdemo, poiščemo poljuben list, ki je potomec vozlišča, ki ga
ţelimo izbrisati. Nato izbrišemo ţeleno vozlišče s ključem 5 in na njegovo mesto
vstavimo list. V našem primeru smo izbrali list s ključem 29. Kazalec, ki je kazal
na list vozlišča 25, postavimo na NULL.
31
Slika 16: Izbris elementa 5
Slika 17: Digitalno drevo po izbrisu
32
5 TRIE
Pod imenom trie se skriva struktura za shranjevanje drugačnih ključev. Ključi so
shranjeni v listih drevesa, notranje vozlišče pa so uporablja le za vzdrţevanje
strukture. Ta podatkovna struktura se uporablja za shranjevanje besednih nizov.
Izraz »trie« je predlagal E. Fredkin leta 1960. Iz sredine besede »retrieval«
(pridobivanje podatkov) je izluščil besedo »trie« [1].
Razen tega, da digitalno drevo hrani binarna števila, trie pa besedne nize, je trie
vsaj v teoriji v bistvu enak digitalnemu drevesu. V praksi pa obstaja precejšnja
razlika, saj so običajno deli ključev, s katerimi bomo imeli opravka, kar črke
abecede. To posledično pomeni, da bi imelo v primeru angleške abecede vsako
vozlišče 26 otrok, namesto dveh, kot jih ima digitalno drevo. To lahko prikaţemo
na primeru, ki ga kaţe spodnja slika.
Slika 18: Vozlišče v trie
Vozlišče je sestavljeno samo iz kazalcev. Prvih 26 je v nekakšnem polju; male
črke v vsakem od kazalcev otrok kaţejo na to, kar je napisano pod njimi. Zadnji
kazalec kaţe na zapis, ki vsebuje podatke. To je list, v katerem je shranjen
element. V nadaljevanju si poglejmo, kako uredimo vsebino v strukturi trie.
I. Vse besede, ki se začnejo s črko a, so razvrščene skupaj. Potem sledijo vse
besede, ki se začnejo z b, in tako naprej vse do besed, ki se pričnejo s črko
z.
33
II. Pod skupino besed, ki se začnejo s črko a, vse besede, katerih druga črka je
a, so razvrščene skupaj, nato sledijo vse besede, katerih druga črka je b, in
tako naprej, podobno za vse besede, ki se začnejo na ostale črke abecede.
III. Pod vsako podskupino črk, katerih druga črka je a, so zdruţene vse besede,
katerih tretja črka je a,… in tako naprej [1].
Ker je misel na to, da bi narisali drevo z vsemi 26 kazalci v vozlišču, brezupna,
kot primer podajamo sliko, kjer ima vozlišče v drevesu kazalce od črke a do črke
e. Primer vsebuje besede: bc, dadac in dace.
Slika 19: Primer drevesa trie
Črte med vozlišči kaţejo kako je trie med seboj povezan. Prazna mesta kaţejo na
null.
34
5.1 Iskanje podatkov v drevesu
Za vhod v trie uporabljamo posamezne črke ključa, na podlagi katerih najdemo
pot skozi drevo. Kot primer za iskanje vzemimo besedo lopar iz slike 20. Najprej
bomo iskali v skupini, katere prva črka je l (z uporabo črke l kot zapisa v polju
kazalcev v korenu drevesa). Potem nadaljujemo iskanje v skupini, ki je
podskupina prejšnje skupine, v kateri je druga črka o, nato sledi podskupina, v
kateri je tretja črka p in tako naprej. Če gremo skozi ključ od črke do črke, se
pomikamo navzdol po drevesu. To bi lahko delno primerjali s polnjenjem omare.
Pogledamo v kabinet D (to je najvišja stopnja drevesa), nato najdemo predal A
(naslednja stopnja navzdol). Znotraj tega predala najdete mapo s črko C.
V našem primeru smo imeli samo eno polje, ki ga vsebuje ključ, čeprav bi v
praksi lahko bili še drugi podatki. Kot primer lahko podamo primer slovarja. Če bi
naše drevo vsebovalo slovar, bi obstajala še polja za izgovorjavo, za izpeljanke, za
definicijo besede,…
Splošen opis algoritma za iskanje vpisov v trie je opisan v spodnjih vrsticah:
I. i=0.
II. kazalec kaţe na koren.
III. dokler kazalec ni null in je i manjši kot dolţina ključa k:
i. povečaj i.
ii. nastavi kazalec na ki-ti element polja otroka.
IV. če je kazalec null ali če so podatki trenutnega vozlišča null, potem iskani
vpis v drevesu ne obstaja.
V. sicer podatkovno polje kaţe na ţeleni vnos [1].
Na kratko lahko povemo, da smo našli element v drevesu takrat, če se pot izteče v
listu (če je zadnji znak besede v nekem listu). Če pa se zgodi, da ne moremo
nadaljevati poti po drevesu in nismo porabili vseh znakov besede, pomeni da niza
ni v drevesu.
35
Poglejmo si primer iskanja po drevesu trie.
Slika 20: Drevo trie
Kot lahko vidimo iz slike drevesa, imamo v drevo vstavljene naslednje besede:
lopa, lopar, osa in sars. Poiskati ţelimo besedo lopar. Iskanje pričnemo v korenu
drevesa, kjer poiščemo prvo črko iskane besede (torej črke l). Nato se pomaknemo
po povezavi do naslednjega vozlišča z enakim znakom kot ga ima iskana beseda
(črka o). Tako po enakem postopku iščemo vozlišča iskane besede v drevesu, kot
kaţe slika 21. Ker nobeno od vozlišč ni kazalo na null, pomeni, da smo našli
iskano besedo.
36
Slika 21: Iskanje elementa v drevesu trie
V primeru, da bi iskali besedo lokvanj, bi pri proceduri iskanja našli prve dve črki
v drevesu. Pri preverjali tretje črke (črke k), ne bi našli povezave do iskane črke,
kar bi pomenilo, da iskana črka ne obstaja.
5.2 Vstavljanje podatkov v drevo
Vstavljanje je nekoliko teţje, čeprav gre po zgledu postopka iskanja. Spet se
premikamo po drevesu navzdol, od nivoja do nivoja, kar pa usmerjajo črke
posameznega ţe vstavljenega ključa v drevo. Postopek vstavljanja bomo prikazali
na enakem primeru kot pri postopku iskanja. Ko vpišemo ključ oblike osel,
potrebujemo niz vozlišč, ki bodo zagotovila pot do listov, ki vsebujejo ključ. Na
začetku, ko pot še ne obstaja, jo gradimo sproti. Če korena drevesa ni, ga moramo
ustvariti. Če del poti ţe obstaja, potem ji glavna zanka v proceduri vstavljanja
sledi. Če bo doseţena »slepa ulica« še pred koncem ključa, potem je potrebno
37
ustvariti novo vozlišče, ki bo omogočilo nadaljevanje, ustvarjanje preostale
manjkajoče poti. Ko smo ustvarili novo vozlišče, smo postavili vse njegove
kazalce na null.
Funkcija vstavljanje nam vrne vrednost true, če je bilo vstavljanje uspešno in
vrednost false, če beseda ţe obstaja v drevesu (duplikat).
I. če koren ne obstaja:
i. ustvarimo koren drevesa;
ii. vse kazalce črk korena postavimo na null;
II. drugače za vsako črko iz ključa novega elementa:
i. če kazalec iz tekoče črke kaţe na null ustvarimo novo vozlišče s
kazalci, ki vsi kaţejo na null;
ii. sicer se spustimo v vozlišče, na katero kaţe kazalec;
III. če tekoči kazalec kaţe na null:
i. vstavimo nov element in vrnemo true;
ii. sicer vrnemo false;
Predvidevamo, da koren drevesa ţe obstaja. Vstaviti ţelimo besedo osel. Najprej
preverimo, če obstaja črka o. Črka o ţe obstaja v drevesu, zato preverimo, če
obstaja črka s. Črka s prav tako ţe obstaja v drevesu. Naslednja iskana črka je e,
ker pa pot ţe vstavljene besede osa ne vodi k črki e, moramo ustvariti novo
vozlišče za črko e. Ker ne moremo dopuščati dvojnikov ključev v drevesu, se
moramo pred vstavitvijo novega vpisa prepričati, da na tem mestu ni ničesar. Če
kazalec ni null, potem je beseda ţe v drevesu in vrnemo vrednost false, ki
opozarja klicajoči program, da predmet ni bil vstavljen.
38
Slika 22: Vstavljanje elementa v drevo
5.3 Brisanje podatkov iz drevesa
Začetek postopka brisanja je podoben kot pri postopku iskanja, saj moramo
besedo najprej poiskati v drevesu in šele nato jo lahko izbrišemo. Dokler ima
iskana beseda, ki jo ţelimo izbrisati, enak začetek kot beseda, ki je ţe v drevesu,
se sprehajamo po drevesu od črke do črke. Če je ključ del poti, ki vodi do drugih
ključev, potem moramo izbrisati samo tisto podatkovno vozlišče, ki vsebuje ključ
in z njim povezane podatke. Če se ključ, ki ga ţelimo izbrisati, nahaja na koncu
poti, potem moramo ponoviti naše korake nazaj gor po poti in brišemo pot, dokler
ne pridemo do vozlišča, katerega podatkovno polje ni null. Pozorni moramo biti
na vozlišča, ki imajo dodatno povezavo do drugega vozlišča. V tem primeru jih ne
smemo izbrisati [1].
Funkcija brisanja vrne false, če ni korena drevesa ali če med iskanjem naleti na
vozlišče, ki kaţe na null. Drugače vrne true. Postopek brisanja poteka takole:
39
I. če v drevesu še ni korena, potem funkcija brisanja vrne false;
II. po drevesu iščemo besedo, ki jo ţelimo izbrisati:
i. pomikamo se od vozlišča do vozlišča;
ii. če naletimo na vozlišče, ki kaţe na null, funkcija vrne false, saj
nam kazalec na null kaţe na to, da beseda ne obstaja v drevesu;
iii. če pridemo do konca besede in ta ne kaţe na null in nima otrok,
potem funkcija vrne true (to nam pove, da bodo podatki
izbrisani);
III. brisanje se vrši v nasprotni smeri s pomikanjem nazaj gor po drevesu;
IV. brisanje črk besede poteka tako dolgo, dokler ne naletimo na črko, ki jo
uporablja katera od drugih besed v drevesu (glej sliko 23);
Napisano prikaţimo še na primeru. Izbrisali bomo besedo lopar. Najprej izvedemo
iskanje besede lopar, ko pridemo do konca besede lopar, se prične postopek
brisanja.
40
Slika 23: Brisanje elementa iz drevesa
Brisanje podatkov pričnemo v nasprotni smeri, tako da se pomikamo nazaj gor po
drevesu. Če črka nima povezav na otroke, jo lahko izbrišemo. Najprej izbrišemo
zadnjo črko besede lopar (črko r), nato se pomaknemo en nivo više. Pomaknemo
se po povezavi navzgor po drevesu do naslednjega vozlišča, kjer je črka a. Ker
ima to vozlišče povezavo do nekega drugega znaka, ga ne smemo izbrisati, ker
povezuje besedo lopa. Tukaj se brisanje konča.
Kot smo ţe omenili, sta si v teoriji digitalno drevo in trie podobna. Če si
predstavljamo trie, ki shranjuje binarne podatke, vidimo podobnost. V tem
primeru polje otroka zahteva le dva kazalca, enega za predstavitev 0 in enega za
predstavitev 1. Če bi ju preimenovali na levo in desno, potem podobnost med
njima postane še jasnejša.
41
6 PATRICIA
Ogledali si bomo še zadnjo podvrsto baznih dreves. Patricia je še ena metoda za
pridobivanje niza znakov na podlagi drevesa. Ime patricia izhaja iz kratice:
»Practical Algorithm To Retrieve Information Coded In Alphanumeric«, kar bi v
slovenščino lahko prevedli kot praktični algoritem za vzpostavitev informacij
kodiranih v alfanumerični obliki. Patricia drevo je spremenjena različica
digitalnega drevesa, ki zmanjša število poskusov, potrebnih za premik po drevesu.
Besedilo, ki ga je treba iskati, ni vključeno v drevo in je shranjeno na drugem
mestu, v pomnilniku ali v datoteki. Drevo vsebuje kazalce do ključev v besedilu.
Iskani ključ je najden, če se kjerkoli v besedilu nahaja niz, katerega predpona je
enaka ključu. Omenimo, da to pomeni, da se lahko ujemanje podniza nanaša od
dela, kjer je prišlo do ujemanja s ključem, vse do konca besedilnega niza. Iz tega
sledi, da je patricia drevo prilagojeno za vsako besedilo posebej. Patricia drevesa,
ki nastane iz neke datoteke, ne moremo uporabiti za iskanje v drugi datoteki [1].
O tej metodi izgradnje dreves bomo pričeli razmišljati kot o poenostavljenem
digitalnem drevesu. Pri primeru digitalnega drevesa imamo pogosto vozlišče le z
enim otrokom. V tem primeru se lahko spustimo samo v enega otroka, kljub temu
pa moramo v vozlišču izvesti test. To pomanjkljivost poskušamo izboljšati s
patricia drevesom.
Kot lahko razberemo ţe iz imena te vrste dreves, je ta vrsta dreves oblikovana kot
algoritem, ki znotraj drevesa išče neko besedilo. V spremenjeni obliki lahko
iščemo tudi druge vrste podatkov, ki jih lahko zapišemo z bitnim nizom.
6.1 Pravila patricia drevesa
Kot vsaka vrsta dreves ima tudi patricia drevo svoja pravila, ki se jih moramo
drţati, če ţelimo zgraditi tako drevo, iskati elemente po njem ali jih brisati.
42
I. Vsako vozlišče ima bitni indeks. To je številka, ki zaseda mesto poleg
vozlišča in je potrebna za bitno primerjavo pri operaciji iskanja po
drevesu.
Slika 24: Primer vozlišča
Kot primer si poglejmo sliko 24, ki nam kaţe vozlišče x z bitnim indeksom 2. Pri
izvajanju procedure iskanja pridemo do ţelenega vozlišča, naredimo primerjavo
na podlagi bitnega indeksa s ključem, ki ga iščemo in na podlagi tega se po
drevesu usmerimo levo če je ta 0 in desno če je 1.
II. Bitni indeks danega vozlišča se mora v drevesu zmanjševati, ko se
premikamo navzdol po drevesu. Tako bo bitni indeks roditeljevega
vozlišča v drevesu vedno večji od njegovih otrok. Kot bomo videli
kasneje, je lahko v nekaterih oblikah drevesa tudi ravno obratno.
III. Operacija iskanja po drevesu je zaključena, ko sledimo povezavi navzgor,
saj nas le-ta pripelje nazaj na vozlišče.
IV. Ključi v patricia drevesu so bitni nizi. Bitni indeksi v ključu so označeni v
vrstnem redu od leve proti desni, kot kaţe spodnji primer:
Bitni
indeks: 4 3 2 1 0
Ključ: 1 1 0 0 1
Tabela 3: Zapis ključa
Pri proučevanju drevesa patricia iz različnih literatur in virov smo odkrili nekaj
razlik. Kot prvo smo zaznali razlike glede smeri številčenja bitnega indeksa, saj se
ponekod pojavi štetje od 0 naprej - naraščajoče, drugje pa padajoče, torej proti 0.
43
Do razlike pride tudi pri razumevanju skrajno leve vrednosti, kjer je le-ta ponekod
razumljena kot vrednost 0, drugod pa kot 1. Naslednja razlika se kaţe glede
razvrščanja elementov v drevesu po bitnem indeksu, saj se ponekod pojavi, da
bitni indeks od korena narašča, drugje pa pada. Za laţje razumevanje bomo
povedano prikazali še v tabeli.
Način 1: Način 2:
Številčenje bitnega
indeksa: 6 5 4 3 2 1 0 0 1 2 3 4 5 6
Skrajno leva vrednost: 1 0
Razvrščanje v drevo
glede na bitni indeks:
Koren drevesa ima
največjo vrednost bitnega
indeksa. S pomikanjem
po drevesu le-ta pada.
Koren drevesa ima
najmanjšo vrednost
bitnega indeksa. S
pomikanjem po drevesu
le-ta narašča.
Tabela 4: Načini številčenja bitnega indeksa
V tem diplomskem delu bomo uporabljali način 1.
6.2 Vstavljanje podatkov v drevo in razvijanje drevesa
Obstajata dva postopka povezana z gradnjo te vrste drevesa: vstavi v obstoječe
drevo in vstavi v novo drevo.
V obeh primerih se izvedejo naslednji koraki:
1. izvede se (neuspešen) postopek iskanja ključa, v katerem najdemo
najbliţje vozlišče;
2. poišče se bitni indeks novega vozlišča;
3. poišče se poloţaj novega vozlišča v drevesu;
4. vzpostavijo se povezave;
44
Vstavi v novo drevo:
Ta postopek uporabljamo v primeru, ko smo med iskanjem najbliţjega vozlišča
našli vrednost null.
i. izvedi postopek iskanja po drevesu za novo vozlišče
ii. bitni indeks novega vozlišča je enak poziciji skrajno leve vrednosti v
ključu, ki ima vrednost 1. Za boljše razumevanje si poglejmo naslednji
primer, kjer imamo ključ 00110.
Bitni
indeks: 4 3 2 1 0
Ključ: 0 0 1 1 0
Tabela 5: Zapis ključa
Kot smo ţe povedali, je bitni indeks vozlišča tisti indeks, ki ima prvo
skrajno levo vrednost enako 1. Iz tabele lahko vidimo, da je v našem
primeru bitni indeks enak 2.
iii. vstavimo vozlišče v drevo. Imamo samo eno mesto za vstavitev novega
elementa.
iv. vzpostavitev povezave. Za novo vozlišče v novo osnovanem drevesu bo
leva povezava vedno nepovezana, desna povezava pa bo vedno povezava
navzgor, ki kaţe sama nase.
Slika 25: Vozlišče po vzpostavitvi povezav
45
Vstavi v obstoječe drevo:
Ta postopek se uporablja za večino vstavitev vozlišča v drevo.
i. izvedi postopek iskanja novega vozlišča po drevesu. Ker vozlišča, ki ga
ţelimo vstaviti, še ni v drevesu, iskanje zaključimo z znanim najbliţjim
vozliščem in ga bomo uporabili za ugotovitev bitnega indeksa.
ii. bitni indeks novega vozlišča je skrajno levi bit, kjer se ključ novega
vozlišča in ključ najbliţjega vozlišča razlikujeta. Kot primer vzemimo
novo vozlišče X - 00101, najbliţje vozlišče Y – 00011.
Bitni
indeks: 4 3 2 1 0
Ključ X: 0 0 1 0 1
Ključ Y: 0 0 0 1 1
Tabela 6: Dvojiški zapis ključa x in y
V našem primeru bo bitni indeks ključa X enak 2, saj je skrajno levi bit,
kjer se ključ X in Y razlikujeta, na poziciji 2.
iii. vstavimo vozlišče v drevo. Mesto, kamor smo vstavili vozlišče, je odvisno
od bitnega indeksa novega vozlišča. Če je niţji od tistega najbliţjega
vozlišča, potem ga vstavimo pod njega, sicer pa nad njega.
a) nov bitni indeks < najbliţji bitni indeks:
Če je bitni indeks novega vozlišča niţji, potem ga lahko enostavno
vstavimo na mesto povezave navzgor, ki je rezultat neuspešnega
iskanja.
b) nov bitni indeks > najbliţji bitni indeks:
To je lahko včasih teţavno, saj se moramo za to, da najdemo ustrezno
mesto za novo vozlišče, pomikati nazaj navzgor po drevesu. Novo
46
pozicijo določimo s premikanjem navzgor po drevesu do najbliţjega
vozlišča in vstavimo novo vozlišče na prvo veljavno mesto določeno
na podlagi bitnega indeksa.
iv. določitev povezav. Ena od povezav bo vedno vodila nazaj na novo
vozlišče, kar je določeno s ključem novega vozlišča in njegovega bitnega
indeksa. Če je vrednost na bitnem indeksu novega vozlišča enaka 0, potem
bo leva povezava kazala nazaj na novo vozlišče. Če pa ima vrednost 1,
potem bo pa desna povezava kazala nazaj navzgor, nase. Druga povezava
bo kazala bodisi nazaj na najbliţje vozlišče (za en nivo gor ali dol), ali, če
smo se morali pomakniti gor po drevesu za nekaj nivojev, bo druga
povezava kazala na vozlišče pod novim.
Da bi do sedaj povedano teorijo še bolje razumeli, bomo razloţeno teorijo
prikazali še na podlagi praktičnega primera. V patricia drevo bomo vstavili
podatke, ki jih kaţe spodnja tabela:
Bitni
indeks: 4 3 2 1 0
Ključ A: 0 1 0 0 1
Ključ B: 1 1 1 0 1
Ključ C: 1 0 1 0 1
Ključ D: 0 1 1 1 0
Ključ E: 0 1 0 1 1
Tabela 7: Ključi, ki jih bomo vstavili v patricia drevo
Vstavimo ključ A – 01001:
Ker drevo še ne obstaja, ne izvajamo procedure iskanja. Uporabili bomo postopek
vstavi v novo drevo.
i. poiščemo bitni indeks. V našem primeru je skrajno levi bitni indeks, ki ima
vrednost enako 1, enak 3.
47
ii. vzpostavimo povezavo. Kot smo zapisali, je skrajno leva povezava
nepovezana (za novo vozlišče drevesa), desna pa kaţe nazaj na samega
sebe, saj je vrednost bitnega indeksa enak 1, kar pomeni, da gremo v desno
stran.
Slika 26: Vstavitev ključa A
Vstavimo ključ B – 11101:
i. izvedemo iskanje ključa B v našem ţe obstoječem drevesu. Začnemo pri
korenu drevesa (to je ključ A), ki ima bitni indeks 3. Preverimo kakšno
vrednost nosi bitni indeks 3 v ključu B. Ker je omenjeni bit ključa B enak
1, gremo naprej po drevesu v desno poddrevo, kjer pa naletimo na
povezavo, ki kaţe nazaj na ključ A. Tako vemo, da je ta ključ naše
najbliţje vozlišče, zato uporabimo obstoječi postopek gradnje drevesa.
ii. poiščemo bitni indeks. Primerjamo ključa A in B in iščemo najbolj levi bit,
v katerem se omenjena ključa razlikujeta.
Bitni
indeks: 4 3 2 1 0
Ključ A: 0 1 0 0 1
Ključ B: 1 1 1 0 1
Tabela 8: Primerjava ključev A in B
Najbolj levi bit, kjer se ključa razlikujeta, je bit 4, torej ima ključ B bitni
indeks 4.
48
iii. poiščemo poloţaj. Ker je bitni indeks ključa B večji od bitnega indeksa
ključa A, mora biti ključ B v drevesu vstavljen nad ključ A. Premikamo se
gor po drevesu, da najdemo prvo razpoloţljivo mesto. V tem primeru je to
mesto direktno nad ključem A.
iv. vzpostavitev povezave. Na bitnem indeksu 4 ima ključ B vrednost 1. To
nam pove, da bo desna povezava kazala nazaj na ključ B, leva povezava pa
bo kazala na ključ A.
Slika 27: Patricia po vstavitvi ključa B
Vstavimo ključ C – 10101:
i. spet najprej izvedemo postopek iskanja po drevesu, da bi našli ključ C.
Pričnemo pri korenu drevesa in preverimo številko bitnega indeksa korena,
ki je v našem primeru 4. Nato preverimo kakšno vrednost vsebuje ključ C
na bitnem indeksu 4. Ker vsebuje vrednost 1, pomeni, da gremo na desno.
Tu naletimo na povezavo, ki nas vodi nazaj na ključ B. To nam pove, da je
ključ B naše najbliţje vozlišče.
ii. poiščemo bitni indeks za ključ C. Iščemo najbolj levi bit, kjer se ključa C
in B razlikujeta.
49
Bitni
indeks: 4 3 2 1 0
Ključ B: 1 1 1 0 1
Ključ C: 1 0 1 0 1
Tabela 9: Primerjava ključev B in C
Tabela 9 kaţe, da je najbolj levi bit bit 3. Zato je bitni indeks vozlišča, ki
vsebuje ključ C, enak 3.
iii. vstavimo vozlišče. Ker je bitni indeks novega vozlišča 3 in je manjši od
indeksa ključa B (ki je 4), lahko C enostavno vstavimo na povezavo, ki
smo ji sledili in nas je pripeljala nazaj na ključ B in je bila desna povezava
ključa B.
iv. vzpostavimo povezavo. Ključ C ima na bitnem indeksu 3 vrednost 0, zato
postane leva povezava tista, ki kaţe sama nase, ključ B pa dobi desno
povezavo.
Slika 28: Patricia po vstavitvi ključa C
Vstavimo ključ D – 01110:
i. najprej izvedemo iskanje ključa D v drevesu. Iskanje pričnemo v korenu
drevesa, kjer nas ključ D pripelje do ključa A (ključ D ima vrednost 0 na
bitu 4, zato gremo levo). Preverimo vrednost bitnega indeksa 3 ključa D.
50
Ta ima vrednost 1, zato gremo desno. Povezava na desni nas pripelje nazaj
na ključ A. To pomeni, da je naše najbliţje vozlišče, vozlišče s ključem A.
ii. poiščemo bitni indeks ključa D. Najbolj levi bit, kjer se razlikujeta ključa
D in A, ima indeks 2. Zato bitni indeks vozlišča D postane 2.
iii. poiščemo poloţaj volišča. Ker je bitni indeks novega vozlišča 2 in je
manjši od indeksa vozlišča A, lahko D vstavimo kar pod vozlišče A. Tako
vozliče D nadomesti povezavo, ki je kazala nazaj na A.
iv. vzpostavitev povezave. Ključ D ima na bitnem indeksu 2 vrednost 1, zato
dobi desno povezavo, leva povezava vozlišča D pa bo kazala na vozlišče
A.
Slika 29: Patricia po vstavitvi ključa D
Vstavimo ključ E – 01011:
i. postopek iskanja ključa E nas vodi skozi pot B→A→D→A, zato je
vozlišče A naše najbliţje vozlišče.
ii. iščemo bitni indeks. Bitni indeks vozlišča E je skrajno levi bit, kjer se
ključa E in A razlikujeta. Razlikujeta se na bitnem indeksu 1.
51
iii. vstavimo E v drevo. Bitni indeks ključa E je 1, kar pomeni, da moramo
vstaviti E pod vozlišče A. Ker ima A pod sabo ţe vozlišče D z bitnim
indeksom 2, ki je spet večji od bitnega indeksa ključa E, se spet
pomaknemo za en nivo niţe. Na vozlišču D povezava, ki je kazala nazaj na
A, sedaj nadomesti naše novo vozlišče.
iv. vzpostavitev povezave. Ker ima E vrednost 1 na bitnem indeksu 1, bo
desna povezava kazala nazaj na E, leva povezava bo vodila nazaj na ključ
A.
Slika 30: Patricia po vstavitvi ključa E
Poudariti je treba še dejstvo, ki ga moramo upoštevati, saj patricia drevesa, kot
ostala digitalna iskalna drevesa ne obravnavajo dvojnikov. Zato imejmo v mislih,
da ne moremo vstaviti duplikatov ključev v patricia drevo brez nevšečnosti.
Algoritem vstavljanja novega elementa v patricia drevo bi lahko povzeli takole:
I. če koren še ne obstaja, potem je novo vozlišče koren.
II. drugače iščemo v obstoječem drevesu ključ novega elementa.
52
III. če iskanje vrne kazalec na ključ, potem je novi ključ duplikat, in
zaključimo.
IV. drugače najdi vozlišče, ki se zadnje ujema v bitnem vzorcu s ključem
novega vozlišča (mesto prvega različnega bita).
V. ustvari novo vozlišče:
i. bitni indeks kaţe mesto prvega različnega bita.
ii. novo vozlišče je eno od njegovih lastnih otrok.
VI. vključi novo vozlišče v drevo.
Algoritem vstavljanja v drevo lahko implementiramo na naslednji način.
Napišemo ga kot bool funkcijo, saj njegova vrnjena vrednost pove ali je bilo
vstavljanje uspešno ali ne. Funkcija vrne true, če je bilo vstavljanje uspešno in
false, če je ključ duplikat ali predpona ţe obstoječega ključa v drevesu.
Patricia drevesa so načeloma dobro uravnoteţena. Celovita analiza patricia
drevesa je preteţka, da bi jo na enostaven način razloţili. V splošnem velja, da so
stroški vstavljanja in iskanja O(log n). Pri tem je pomembno, da v algoritmu
uporabljamo le bitne primerjave in ne primerjave nizov.
6.3 Iskanje podatkov v drevesu
V patricia drevo so lahko vstavljene posamezne črke, besede ali besedni nizi, ki se
obravnavajo kot nizi črk. Vsako vozlišče je označeno z bitnim indeksom, kar je
treba upoštevati pri odločanju, v katero smer bo šla razvejitev v drevesu. Če
vstavljamo cele besede, potem je smiselna ideja, da po vsakem besednem ključu
dodamo presledek. To nam pomaga, da lahko ločujemo trenutni ključ od ključa, ki
sledi. Poglejmo primer. Če sta v drevesu ključa JAN in JANEZ brez dodanega
presledka, drevo razume besedo JAN kot predpono ključa JANEZ. Besede JAN
ne more shraniti.
53
Za primer iskanja vzemimo patricia drevo s slike 30. Predpostavimo, da bomo
iskali ključ E. Bitni indeks vozlišča B pove, kateri bitni indeks moramo preveriti v
ključu E. Ključ E ima naslednji bitni niz: 01011. Preveriti moramo vrednost
bitnega indeksa 4. Bitni indeks 4 ključa E je 0, kar pomeni, da se bomo pomaknili
v levo stran. Leva povezava vozlišča B nas popelje do vozlišča A, kjer bomo
preučili bitni indeks 3. Ključ E ima v tem bitnem indeksu vrednost 1, zato gremo
desno. Desna povezava vozlišča A nas pripelje do vozlišča D, kjer preverimo bitni
indeks 2. Vrednost bitnega indeksa ključa E je 0, zato gremo levo. Povezava nas
pripelje do vozlišča E, kjer moramo preveriti bitni indeks 1. Vrednost v tem
bitnem indeksu je 1, kar pomeni, da se bomo pomaknili desno. Pomik v desno nas
pripelje nazaj k istem vozlišču, kar pomeni, da smo našli ţelen ključ in tako lahko
zaključimo postopek iskanja.
Na kratko lahko zapišemo algoritem iskanja takole:
I. začnemo pri korenu in če ţe obstaja preverimo njegov bitni indeks.
II. ponavljamo dokler tekoči kazalec ne kaţe navzgor:
i. preverimo bitni indeks vozlišča;
ii. glede na vrednost bitnega indeksa se pomaknemo v levo ali
desno stran (v levo, če ima bitni indeks vrednost 0 in v desno,
če ima vrednost 1);
54
7 OPIS PROGRAMA
V programskem jeziku C++ smo realizirali program za delo z digitalnimi drevesi.
V programu lahko izvršimo postopke gradnje oziroma vstavljanja v digitalno
drevo, iskanje po drevesu, brisanje podatka iz drevesa ter izpis drevesa. V meniju
moramo izbrati številko za izvajanje posameznega postopka. 1 - za vstavitev
podatka v digitalno drevo, 2 – za izpis digitalnega drevesa, 3 – za iskanje
elementa po digitalnem drevesu, 4 – za brisanje podatka iz digitalnega drevesa in
5- za izhod iz programa.
Slika 31: Meni programa gradnje digitalnega drevesa
7.1 Vnos podatkov in potek programa
Preden lahko izvršimo postopke iskanja, brisanja in izpisovanja, moramo vstaviti
podatke v drevo. Zato je prvi potreben korak vnos podatkov v drevo.
Podatke vnašamo v digitalno drevo z vpisom številke iz menija, ki predstavlja
vstavljanje podatka, tj. 1. Vstavimo prvi podatek, ki postane koren drevesa, ostali
podatki se vstavljajo glede na bitne vrednosti na posameznem bitu.
55
Slika 32: Vstavljanje podatka v drevo
Program se izvaja tako dolgo, dokler ne vpišemo številko za izhod iz programa, tj.
številko 5.
7.2 Delovanje programa za primer digitalnega drevesa
Kot smo ţe omenili, mora digitalno drevo vsebovati podatke, ki jih lahko potem
izpišemo, iščemo in brišemo. V drevo smo en za drugim vstavljali naslednje
podatke: 24, 17, 36, 82, 19, 38, 15 in 45.
Vstavljanje podatkov:
Postopek vstavljanja podatka vstavi podatek v drevo glede na njihovo vrednost v
bitu 0, bitu 1, bitu 2, itd. Če je vrednost bita 0 enaka 0, potem bo podatek
vstavljen v levo poddrevo, drugače pa v desno poddrevo. Če drevo še ne obstaja
(koren drevesa ne obstaja), potem bo prvi vstavljeni podatek postal koren drevesa.
Drugi vstavljeni element se bo ţe vstavljal po pravilu o vrednosti bita. Ker ima
podatek 17 (100012) na bitu 0 vrednost 1, bo vstavljen v desno poddrevo.
Postopek vstavljanja se nadaljuje po povedanem pravilu. Pred vsako vstavitvijo
novega podatka preverimo, ali podatek, ki ga ţelimo vpisati, ţe obstaja v drevesu,
saj ne dopuščamo dvojnikov v drevesu. Če ţelimo vstaviti podatek, ki ţe obstaja v
drevesu, nam program javi opozorilo, da števila ne moremo vstaviti, ker je ţe v
drevesu. Pri vsakem vpisu podatka v drevo se izpiše dvojiški zapis podatka, v
56
obratnem zaporedju ničel in enic (razen pri prvem elementu, ki postane koren
drevesa).
Izpis podatkov:
Izpis digitalnega drevesa izvajamo po principu LSD, najprej levi otrok, nato
roditelj in na koncu še desni otrok. Tako bi izpis digitalnega drevesa prej podanih
podatkov izgledal tako, kot kaţe slika 34.
Slika 33: Izpis drevesa
Iskanje podatkov:
Iskanje podatka v digitalnem drevesu poteka po postopku pregleda dvojiškega
zapisa iskanega podatka. Vzemimo, da ţelimo poiskati podatek 82. Dvojiški zapis
števila je 10100102. Postopek iskanja podatka poteka tako dolgo, dokler koren ne
kaţe na NULL in dokler vrednost korena ni enaka vrednosti podatka, ki smo ga
vstavili. Če koren vozlišča kaţe na NULL, pomeni, da podatka ni v drevesu in
funkcija bool vrne false. Funkcija vrne true, če je bil iskani podatek najden.
57
Slika 34: Iskanje podatka v drevesu
Če je vrnjena vrednost true, se izpiše Stevilo je v drevesu!, sicer se izpiše Stevila
ni v drevesu!.
Brisanje podatkov:
Vzemimo, da ţelimo izbrisati vozlišče s ključem 36. Najprej moramo poiskati
ţeleno vozlišče v drevesu. Ko ga najdemo, si v nov kazalec shranimo njegovo
mesto, da lahko pozneje vanj zapišemo vrednost lista, s katerim ga zamenjamo.
Nato poiščemo list, ki je potomec vozlišča, ki ga ţelimo izbrisati. V vozlišče, ki
ga ţelimo izbrisati vpišemo vrednost prej iskanega lista. Ker smo list pomaknili
na mesto izbrisanega, moramo kazalec, ki je kazal na ta list nastaviti na NULL.
Slika 35: Določitev števila za izbris
58
Slika 36: Izpis drevesa po brisanju
59
ZAKLJUČEK
Drevesa so med najbolj uporabnimi podatkovnimi strukturami na področju
računalništva. V diplomskem delu smo opisali manj znano obliko dreves, ki se
imenujejo bazna drevesa. Podrobni obravnavi različnih tipov baznih dreves je
sledila v programskem jeziku C ++ realizirana ena od podvrst baznih dreves.
Najprej smo za laţje razumevanje nadaljnje snovi razloţili pojme algoritem,
podatkovna struktura in drevo. Kot osnovo oziroma rdečo nit nadaljnjih poglavij,
smo najprej obdelali in razloţili urejanje z radiksom, ki je na nek način predpogoj
za razumevanje baznih dreves.
Osnovna oblika baznih dreves temelji na dvojiških drevesih, ki hranijo podatke v
drevesu na podlagi posameznih bitov v bitnem nizu. Prednost te vrste dreves je v
dejstvu, da ni potrebna uporaba celega ključa, ampak samo del ključa. Potrebno je
omeniti tudi dejstvo, da ne obstaja dogovor kako številčiti bite ključa. Nekateri jih
številčijo od leve strani proti desni, drugi jih številčijo od desne proti levi. Če
izberemo moţnost številčenja od leve proti desni strani, potem moramo poznati
najdaljši ključ, saj moramo poznati skrajno levo pozicijo bita. Nekateri jih začnejo
številčiti z 0, drugi pa z 1, torej to pomeni, da je kot prvi bit razumljeni bit 0 ali da
je kot prvi bit razumljeni bit 1.
Bazna drevesa obsegajo tri pomembne podvrste, in sicer: digitalno drevo, trie in
patricia drevo.
Prva obravnavana podvrsta baznih dreves so digitalna drevesa. Za digitalna
drevesa velja, da so ključi predstavljeni kot binarni nizi. Digitalna drevesa se ne
izkaţejo najbolje, če so ključi, ki jih vstavljamo v drevo, nizi znakov. V tem
primeru je premikanje in testiranje bitov bolj zapleteno, saj moramo takrat vzeti v
obzir število bitov za znak (sedem ali osem) in moramo nekako urediti premik za
celotni niz, ko smo prišli na konec znaka. Za shranjevanje niza znakov, je zato
vpeljano drevo trie. Trie je struktura shranjevanja ključev samo v listih drevesa,
saj so notranja vozlišča uporabljena le za vzdrţevanje strukture in ta struktura se
60
uporablja za shranjevanje besednih nizov. V teoriji izgledala obe prej omenjeni
podvrsti baznih dreves zelo podobni. Ločuje ju dejstvo, da digitalno drevo
razpolaga z binarnimi števili, trie pa z besednimi nizi. Razlika se kaţe tudi v
številu otrok, ki ga ima vozlišče v posamezni podvrsti. Vozlišče v digitalnem
drevesu ima največ dva otroka, medtem, ko ima v trie vsako vozlišče 26 otrok,
oziroma toliko, kolikor je črk v abecedi.
Na koncu je opisana zadnja podvrsta baznih dreves, patricia drevo. Ime patricia
izhaja iz kratice: »Practical Algorithm To Retrieve Information Coded In
Alphanumeric«, kar bi v slovenščino lahko prevedli kot praktični algoritem za
vzpostavitev informacij kodiranih v alfanumerični obliki. Patricia drevo je
spremenjena različica digitalnega drevesa, ki zmanjša število poskusov, potrebnih
za premik po drevesu. Značilnost te strukture je, da v resnici ni drevo, saj zaradi
povezav, ki jih vsebuje, lahko nastanejo cikli. Ker pa se v večini primerov
uporablja kot drevo, jo kljub temu imenujemo »drevo«.
V zadnjem poglavju smo opisali program za gradnjo in urejanje digitalnega
drevesa. Program smo predstavili na podlagi konkretnega primera. Programsko
kodo programa smo dodali kot prilogo.
61
LITERATURA
[1] Parsons, T. W. Introduction to algorithms in Pascal, J. Wiley & Sons, New
York, 1995.
[2] Sedgewick, R. Algorithms in Java, Third edition (Parts 1-4): Fundamentals,
Data Structures, Sorting, Searching, Addison-Wesley, New York, 2003.
Pridobljeno 16.3.2010, iz
http://books.google.si/books?id=hyvdUQUmf2UC&printsec=frontcover&sou
rce=gbs_v2_summary_r&cad=0#v=onepage&q=&f=false
[3] Kozak, J. Podatkovne strukture in algoritmi, DMFA Slovenije, Ljubljana
1997.
[4] Kononenko, I., Robnik Šikonja, M. in Bosnić, Z. Programiranje in algoritmi,
Fakulteta za računalništvo in informatiko, Ljubljana, 2008.
[5] Domiter, J. Algoritmi urejanja, diplomsko delo, Pedagoška fakulteta, Maribor,
2005.
[6] Allison, L. Algorithms and data structures. Pridobljeno 20.3.2010, iz
http://www.csse.monash.edu.au/~lloyd/tildeAlgDS/Sort/Radix/.
[7] Radix trees. (b.d.). Pridobljeno 20.3.2010, iz
http://www.doc.ic.ac.uk/~ajf/Teaching/Haskell/Revision3.pdf.
[8] Felstead, D. How to build Patricia Tries. Pridobljeno 25.4.2010, iz
http://goanna.cs.rmit.edu.au/~stbird/Tutorials/patricia.pdf.
62
PRILOGA
Program, ki smo ga predstavili v sedmem poglavju, je v prilogi zapisan v celoti, v
programskem jeziku Microsoft Visual C++.
#include "stdafx.h"
#include <iostream.h>
#define x 8
class CVozlisce
{
public:
{konstruktor, ki ustvari vozlišče s podatkom podatek}
CVozlisce(int podatek)
{
kljuc=podatek;
levi_otrok=NULL;
desni_otrok=NULL;
}
{konstruktor, ki ustvari vozlišče s podatkom podatek in kazalcem na levega in
desnega otroka}
CVozlisce(int podatek, CVozlisce* levi, CVozlisce* desni)
{
kljuc=podatek;
levi_otrok=levi;
desni_otrok=desni;
}
~CVozlisce()
{
if (levi_otrok!=NULL) delete levi_otrok;
if (desni_otrok!=NULL) delete desni_otrok;
}
int VrniPodatek() {return kljuc;} {vrnemo podatek v vozlišču}
CVozlisce* VrniLevegaOtroka(){return levi_otrok;} {vrnemo kazalec na levega
otroka vozlišča}
CVozlisce* VrniDesnegaOtroka(){return desni_otrok;} {vrnemo kazalec na
desnega otroka vozlišča}
63
void SpremeniPodatek(int podatek) {kljuc=podatek;}
void SpremeniLevega(CVozlisce* levi) {levi_otrok=levi;} {spremenimo kazalec,
ki kaže na levega otroka vozlišča}
void SpremeniDesnega(CVozlisce* desni) {desni_otrok=desni;} {spremenimo
kazalec, ki kaže na desnega otroka vozlišča}
private:
int kljuc;
CVozlisce* levi_otrok;
CVozlisce* desni_otrok;
};
class CDrevo
{
public:
CDrevo() {koren=NULL;} {konstruktor, ki ustvari prazno drevo}
~CDrevo() {delete koren;}
CVozlisce* VrniKoren() {return koren;} {vrne kazalec na koren}
bool Prazen() {metoda, kjer vrnemo ali je drevo prazno}
{
if (koren==NULL)
return true;
else
return false;
}
void SpremeniKoren(CVozlisce* k) {koren=k;} {spremeni koren}
void Izpisi(CVozlisce* k); {izpiše elemente drevesa}
CVozlisce* koren;
};
void CDrevo::Izpisi(CVozlisce* k) {izpis digitalnega drevesa}
{
if (k!=NULL)
{
Izpisi(k->VrniLevegaOtroka()); {izpis levega otroka}
cout << k->VrniPodatek() << " "; {izpis starša}
Izpisi(k->VrniDesnegaOtroka()); {izpis desnega otroka}
}
}
64
class CDigitalno_drevo : public CDrevo
{
public:
CDigitalno_drevo() {}
~CDigitalno_drevo() {}
void Vstavi(int podatek, int polje[x]); {metoda vstavi podatek v drevo}
bool Iskanje(int podatek, int polje[x]); {metoda poišče podatek v drevesu}
void Odstrani(int podatek, int polje[x]); {metoda odstrani podatek iz drevesa}
void Binarno(int podatek, int polje[x]); {metoda pretvori decimalno število v
dvojiško}
};
{metoda pretvori decimalno število v dvojiško in ga zapiše v polje}
void CDigitalno_drevo::Binarno(int podatek, int polje[x])
{
for (int i=0; i<x;i++) {ustvarimo polje samih ničel}
{
polje[i]=0;
}
cout<<"\n";
int ostanek;
int z=podatek;
{v polje se shrani dvojiški zapis podatka, v obratnem vrstnem redu}
for(i=0; i<x; i++)
{
if (z > 0)
{
ostanek=z%2;
z=z/2;
if (polje[i]!=ostanek) {če vrednost v polju ni enaka nič}
{
polje[i]=ostanek; {se spremeni}
}
else
{
polje[i]=ostanek; {drugače ostane enaka}
}
}
}
}
65
{metoda vstavi element v drevo}
void CDigitalno_drevo::Vstavi(int podatek, int polje[x])
{
if (VrniKoren()==NULL)
{
CVozlisce* koren = new CVozlisce(podatek,NULL,NULL);
SpremeniKoren(koren);
}
else
{
CVozlisce* k=VrniKoren();
int i=0;
while(i<8)
{
if(polje[i]==0)
{ {če levi otrok še ne obstaja}
if (k->VrniLevegaOtroka()==NULL)
{
CVozlisce* novi = new CVozlisce(podatek,NULL,NULL);
k->SpremeniLevega(novi);break; {ustvari novo vozlišče,
ki postane levi otrok}
}
else
{
{če levi otrok obstaja, potem se v k shrani njegov kazalec}
k=k->VrniLevegaOtroka();
i++;
}
}
else if(polje[i]==1)
{ {če desni otrok še ne obstaja}
if (k->VrniDesnegaOtroka()==NULL)
{
CVozlisce* novi = new CVozlisce(podatek,NULL,NULL);
k->SpremeniDesnega(novi);break; {ga ustvarimo}
}
else
{
{če desni otrok obstaja, potem se v k shrani njegov kazalec}
k=k->VrniDesnegaOtroka();
i++;
}
}
}
}
}
66
{metoda, s katero poiščemo podatek v drevesu}
bool CDigitalno_drevo::Iskanje(int podatek, int polje[x])
{
CVozlisce* k=VrniKoren();
int preverba=0;
int i=0;
{dokler k ni NULL in vrednost k-ja ni enaka podatku, ki ga iščemo}
while((k!=NULL)&&(k->VrniPodatek()!=podatek))
{
if(polje[i]==0) {če je vrednost v polju enaka 0}
{
k=k->VrniLevegaOtroka(); {kazalec premaknemo na naslednjega
levega otroka}
i++;
}
else
{
if(polje[i]==1)
{
k=k->VrniDesnegaOtroka(); {kazalec premaknemo na
naslednjega desnega otroka}
i++;
}
}
}
if(k==NULL) {če k kaže na null, potem podatka ni v drevesu}
return false;
else
return true; {drugače podatek obstaja v drevesu}
}
{metoda izbriše vozlišče v digitalnem drevesu}
void CDigitalno_drevo::Odstrani(int podatek, int polje[x])
{
CVozlisce* k=VrniKoren();
CVozlisce* zacasni; {kazalec zacasni kaže na roditelja premaknjenega otroka}
CVozlisce* zabrisanje; {kaže na mesto, na katerem je vozlišče za izbris –
zamenjamo ga z listom}
int i;
if((podatek==k->VrniPodatek())&&(k->VrniLevegaOtroka()==NULL)
&&(k->VrniDesnegaOtroka()==NULL)) {če je podatek, ki ga želimo
izbrisati koren in še nima otrok}
{
67
SpremeniKoren(NULL);
cout << "\nKoren drevesa je bil izbrisan!\n";
}
else
{
if((podatek==k->VrniPodatek())&&((k->VrniLevegaOtroka()!=NULL)
||(k->VrniDesnegaOtroka()!=NULL))) {ko iz drevesa brišemo koren, ki
ima vsaj enega otroka}
{
zabrisanje=k;
i=0;
}
else {ko brišemo poljubno vozlišče v levem ali desnem poddrevesu}
{
i=0;
while((k->VrniLevegaOtroka()!=NULL)||
(k->VrniDesnegaOtroka()!=NULL)) {iščemo vozlišče, ki ga želimo
izbrisati}
{
zacasni=k;
if (polje[i]==0) {glede na vrednost bita se pomikamo
levo (0) ali desno (1), dokler ne najdemo vozlišče, ki ga želimo izbrisati}
{
k=k->VrniLevegaOtroka();
i++;
}
else
{
k=k->VrniDesnegaOtroka();
i++;
}
if(podatek==k->VrniPodatek()) {če je vrednost vozlišča, ki
ga želimo izbrisati enaka vrednosti v trenutnem vozlišču, v kazalec zabrisanje
zapišemo mesto vozlišča, ki ga bomo izbrisali. Ko smo ga našli, prekinemo while
zanko}
{
zabrisanje=k;
break;
}
}
}
while((k->VrniLevegaOtroka()!=NULL)||
(k->VrniDesnegaOtroka()!=NULL)) {v poddrevesu vozlišča, ki ga
brišemo, iščemo list}
{
zacasni=k;
68
if(k->VrniLevegaOtroka()!=NULL) {če obstaja levi otrok, se
pomaknemo v levo poddrevo, sicer v desno}
{
k=k->VrniLevegaOtroka();
polje[i]=0;
}
else
{
k=k->VrniDesnegaOtroka();
polje[i]=1;
}
i++;
}
zabrisanje->SpremeniPodatek(k->VrniPodatek()); {v vozlišče, ki je
označeno za brisanje (s kazalcem zabrisanje) vpišemo vrednost najdenega lista}
if(polje[i-1]==0) {zaradi premaknjenega lista na mesto izbrisanega,
moramo roditelju, ki zato nima več otroka, določiti kazalec na null}
{
zacasni->SpremeniLevega(NULL);
}
else
{
zacasni->SpremeniDesnega(NULL);
}
cout << "\nElement je bil izbrisan iz drevesa!\n";
}
}
int main(int argc, char* argv[]){
CDigitalno_drevo Digitalno;
int stevilo;
int izbiranje;
int A[x];
do{
cout << "\n::::::::::DIGITALNO DREVO::::::::::\n";
cout <<"1. Vstavi element.\n";
cout <<"2. Izpisi drevo.\n";
cout <<"3. Iskanje elementa.\n";
cout <<"4. Odstrani element.\n";
cout <<"5. Izhod.\n\n";
cin >> izbiranje;
switch (izbiranje){
case 1: {
69
cout << "Vpisi element:";
cin >> stevilo;
Digitalno.Binarno(stevilo,A);
if (Digitalno.Iskanje(stevilo,A)==true)
{
cout << "Stevila ne mores vstaviti, ker je ze v drevesu!";
cout << "\n";}
else
{
Digitalno.Vstavi(stevilo,A);
cout << "\n";
}
break;}
case 2:{
if (Digitalno.Prazen()==true)
{
cout << "Drevo se ne obstaja!!!\n";
}
else
{
cout << "\n Izpis digitalnega drevesa: ";
Digitalno.Izpisi(Digitalno.koren);
cout << "\n\n";
}
break;}
case 3:{
cout << "Kateri element zelis poiskati: ";
cin >> stevilo;
Digitalno.Binarno(stevilo,A);
if(Digitalno.Iskanje(stevilo,A))
{
cout << "Stevilo je v drevesu!";
cout << "\n\n";
}
else
{
cout << "Stevila ni v drevesu!";
cout << "\n\n";
}
break;}
case 4: {
cout << "Katero stevilo zelis odstraniti: ";
cin >> stevilo;
Digitalno.Binarno(stevilo,A);
70
if(Digitalno.Iskanje(stevilo,A)==true)
{
Digitalno.Odstrani(stevilo,A);
}
else
{
cout << "Tega stevila ni v drevesu";
}
break;}
case 5:
return(0);
cout << "\n";
default:cout << "Napacna stevilka, ni je v meniju!\n";
}
}
while (izbiranje!=5); {ko vtipkamo 5, se program zaključi}
return 0;
}