AVL Stablo - Implementacija - 2. deo - Pretraga
Uvod
U drugom nastavku serijala o implementaciji algoritma AVL bavićemo se pretragom stabla, odnosno, pronalaženjem određenog čvora preko pripisane celobrojne vrednosti.
Osnovna metoda koju ćemo pisati ima povratni tip int
, a konkretne povratne vrednosti, biće:
- celobrojna vrednost koja predstavlja 'dubinu' stabla na kojoj je tražena vrednost pronađena (to jest, broj koraka pretrage) - pod uslovom da je tražena vrednost pronađena
- vrednost
-1
- ako tražena vrednost nije pronađena u stablu
Takođe, budući da se stabla pretrage tipično koriste za pronalaženje obimnijih struktura sa više sadržaja (u takvom stablu, celobrojne vrednosti su samo ključevi za pretraživanje), prikazaćemo i implementaciju sa strukturom čvora koja je dopunjena klasom sa dodatnim podacima.
U navedenim okolnostima, funkcija (naravno) ne vraća celobrojnu vrednosti, već - referencu na pronađeni čvor (ili vrednost null
, ako čvor nije pronađen).
Pretraga stabla - Osnovni princip
Podsetimo se na opšte smernice koje smo naveli u članku o binarnom stablu pretrage:
- pretraga počinje od korenog čvora i praktično se usmerava prema potencijalnoj lokaciji traženog čvora
- ukoliko čvor sa traženom vrednošću nije pronađen na mestu na kome se traži u određenom koraku (za početak u korenom čvoru), prelazi se u levo podstablo - ukoliko je tražena vrednost manja, ili desno podstablo - ukoliko je tražena vrednost veća - i potom se pretraga nastavlja po istom obrascu *
Na kraju pretrage (koja se obavlja po gore opisanoj proceduri), nastaje jedna od dve moguće situacije:
- čvor sa traženom vrednošću uspešno je pronađen u najviše log(n) koraka
- utvrđeno je da tražena vrednost ne postoji u stablu (takođe u najviše logn koraka)
Zarad temeljitosti, prikazaćemo ponovo postupak pretrage binarnog stabla, kroz dva primera (prvo ćemo tražiti element koji postoji u stablu, a zatim i element koji ne postoji u stablu).
Pretraga stabla u kome postoji traženi element
U prvom primeru, tražimo vrednost 17, koja postoji u sledećem stablu ....
Na samom početku, proverava se koreni čvor:
Koreni čvor ne sadrži traženu vrednost, tražena vrednost (17), veća je od vrednosti korenog čvora (10), i stoga se iz pretrage može isključiti koreni čvor i celo levo podstablo korenog čvora ....
.... i potom se pretraga usmerava na desno podstablo (u kome se tražena vrednost nalazi - ili ne nalazi).
Vrednost korenog čvora desnog podstabla (15) ....
.... manja je od 17, i stoga se iz dalje pretrage isključuje: i čvor #15, i levo podstablo čvora #15:
Pretraga prelazi na koren desnog podstabla prethodno ispitivanog čvora ....
.... i budući da je ispitivana vrednost (22), veća od tražene, iz pretrage se isključuje čvor #22 i desno podstablo čvora #22 ....
.... nakon čega se prelazi u levo podstablo.
Na kraju, u korenu levog podstabla čvora #22 ....
.... pronađena je vrednost koju smo naveli kao kriterijum za pretragu:
Tražena vrednost pronađena je iz četiri pokušaja (pri čemu je 4 - visina stabla).
Pogledajmo u nastavku šta se događa ukoliko pokušamo da pronađemo čvor koji ne može biti pronađen.
Pretraga stabla u kome ne postoji traženi element
U drugom primeru tražimo vrednost 25 - koja ne postoji u stablu, i ponovo se prvo proverava koreni čvor:
Budući da je tražena vrednost veća od 10, iz dalje pretrage se isključuje koreni čvor, kao i celo levo podstablo:
Pretraga prelazi na vrh desnog podstabla korenog čvora:
Pošto se na vrhu desnog podstabla nalazi vrednost koja je manja od 25 (čvor #15), iz dalje pretrage se isključuje čvor #15 i njegovo levo podstablo (za sada je sve isto kao u prvoj pretrazi) ....
Pretraga prelazi na desno podstablo ....
.... i budući da je ispitivana vrednost (22), manja od tražene vrednosti, odbacuje se levo podstablo čvora #22 (kao, naravno, i sam čvor #22).
Sledi prelazak na koren desnog podstabla čvora #22 (čvor #24) ....
.... ali, na navedenoj poziciji takođe nije pronađena tražena vrednost ....
Preostaje pokušaj prelaska na desno podstablo čvora #24 ....
.... međutim, budući da na mestu desnog podstabla uopšte nije pronađen ikakav čvor ....
.... može se zaključiti da čvor sa traženom vrednošću (25) - ne postoji u stablu.
Pretraga stabla - Implementacija
Pošto smo se podsetili na osnovne principe pretrage, prikazaćemo kako se mogu implementirati dve varijante funkcije za pretragu:
- prvo ćemo se osvrnuti na osnovnu implementaciju sa 'običnim' celobrojnim čvorovima
- nakon toga, prikazaćemo implementaciju koja koristi složenije čvorove kojima je pripisana klasa sa dodatnim podacima.
Osnovna pretraga
Metodu za pretragu pozivaćemo uz predavanje korenog čvora u svojstvu argumenta (naravno, predaje se i vrednost koju pretražujemo), a prelasci na leva i desna podstabla biće implementirani preko rekurzije.
Za "školsku" implementaciju u kojoj koristimo 'brojač koraka', biće potrebna i pomoćna metoda ProveraCvora
- koja je zapravo radna metoda (koja rekurzivno sprovodi korake pretrage):
Kao što vidimo, metoda Pretraga
poziva radnu metodu ProveraCvora
, uz prethodno resetovanje brojača, a vidimo i to da metoda za proveru čvorova doslovno sprovodi u delo principe koje smo definisali na početku:
- prvi
if
proverava da li čvor koji je predat kao argument - uopšte postoji (ako metoda naiđe na nepostojeći čvor, može se zaključiti da pretraga nije dala rezultat, i stoga metoda treba da vrati vrednost -1) - u sledećem koraku, brojač se uvećava za 1 (i upravo je broj koraka pretrage, podatak koji osnovna verzija metode za pretragu potencijalno treba da vrati)
- ako je vrednost pronađena (komentar #3), metoda vraća broj koraka u pronalaženju tražene vrednosti, a ako vrednost nije pronađena, preostaje neki od poslednja dva koraka (komentari #4 i #5):
- ako je tražena vrednost manja, pretraga se usmerava na levo podstablo
- ako je tražena vrednost veća, pretraga se usmerava na desno podstablo
Bez korišćenja dve metode (budući da koristimo rekurziju), ne bismo mogli realizovati resetovanje brojača pri svakom pozivu, na iole praktičan i elegantan način.
Pretraga polja baze podataka čiji su ključevi čvorovi AVL stabla
U drugom primeru (koji je, reklo bi se, konkretniji i praktičniji), koristićemo AVL stablo za pronalaženje slogova koji koriste čvorove AVL stabla kao ključeve.
Dodaćemo u program (privremeno, samo za ovaj primer), klasu Osoba
....
.... koju ćemo povezati sa klasom Cvor
, posle čega će stablo imati sledeću strukturu ....
Sada možemo zapisati klasu Osoba
koja će sadržati (glavne) podatke koje skladištimo, i možemo (privremeno) dopuniti klasu Cvor
:
Metoda za pretragu takođe će pretrpeti izmene.
Povratna vrednost biće:
- referenca na objekat klase
Osoba
(čiji id će biti 'propušten' kroz AVL stablo kao kriterijum za pretragu) - ukoliko se pronađe osoba sa datim id-om - objekat sa sistemskom vrednošću
null
- ukoliko osoba sa traženim id-om nije pronađena
Budući da ovoga puta ne koristimo brojač koraka, sve se može realizovati preko (samo) jedne rekurzivne metode:
Kao što vidimo, pretraga čvorova koji 'nose' dodatne podatke, takođe sprovodi u delo principe koji su opisani u uvodnim poglavljima (po svojoj strukturi, poslednja funkcija koju smo videli je gotovo identična prvobitno prikazanoj funkciji za pretragu 'običnih' celobrojnih čvorova, ali, u praktičnom smislu - znatno je svrsishodnija).
Sledeći koraci ....
Verujemo da će stariji i iskusniji čitaoci, po želji (ili, još bolje, u skladu sa potrebama, nekog samostalnog projekta i sl. :)), biti u stanju da preprave i ostale metode koje ćemo implementirati, tako da budu prilagođene radu sa kompleksnijim čvorovima koji nose dodatne podatke, tj. objekte. *
Što se tiče budućih članaka, vratićemo se (zarad preglednosti), na pisanje jednostavnijih funkcija koje ne vraćaju pokazivače (već int
vrednosti).
U sledećem nastavku obradićemo četiri metode za obilazak stabla
- preko algoritma BFS
- preko algoritma DFS - u tri varijante (preorder, inorder i postorder)
Do tada, za vežbu, probajte da implementirate metodu pretrage na iterativni način (bez rekurzije, uz upotrebu petlji).