Algoritmusok és adatszerkezetek 1. > Fák > Bináris keresőfa
Egy bináris fát keresőfának nevezünk, ha minden belső csúcsára igaz, hogy a csúcs bal részfájában minden csúcs kulcsa kisebb, a jobb részfájában minden csúcs kulcsa nagyobb és minden kulcs egyedi a fában.
Egy bináris fát rendezőfának nevezünk, ha minden belső csúcsára igaz, hogy a csúcs bal részfájában minden csúcs kulcsa kisebb vagy egyenlő, a jobb részfájában minden csúcs kulcsa nagyobb vagy egyenlő és lehetnek duplikált kulcsok a fában.
A bináris keresőfát inorder bejárással kiíratjuk egy szigorúan monoton növekvő sorozatot kapunk.
A leggyakoribb művelete a fában a tárolt kulcsok keresése. Az algoritmus a gyökérnél kezdi a keresést, és egy lefelé haladó utat jár be a fában. Minden érintett csúcsnál összehasonlítja a paraméterként kapott kulcsot a csúcs kulcsával. Ha a két kulcs egyenlő, a keresés befejeződik. Ha a paraméterként kapott kulcs kisebb, mint a csúcs kulcsa, akkor a csúcs bal oldali részfájában folytatódik a keresés, ha pedig nagyobb, akkor a jobb oldali részfájában. Ha nullpointerhez értünk a keresés sikertelen.
| $search(t : Node^*; k : \mathcal{T}) : Node^*$ |
| $t \neq \emptyset \land t \rightarrow key \neq k$ | ||||
|
$k < t \rightarrow key$
|
||||
| $t := t \rightarrow left$ | $t := t \rightarrow right$ | |||
| $\text{return} \space t$ | ||||
Megváltoztatja a bináris keresőfával ábrázolt dinamikus halmazt. A módosítást természetesen úgy kell végrehajtani, hogy a bináris keresőfa tulajdonság megmaradjon.
A gyökértől indulunk és megyünk lefelé a kulcsösszehasonlításokkal mindaddig, amíg nullpointert nem találunk. Pontosan ennek a nullpointernek a helyére kell beilleszteni a kulcsot. Viszont, ha a kulcsösszehasonlítások során a kulcsok egyenlőek, akkor az azt jelenti, hogy a kulcs már benne van a fában. Mivel a keresőfában minden kulcs egyedi, így nem kell beszúrni ezt a kulcsot.
| $insert(\&t : Node^*; k : \mathcal{T})$ |
|
$t = \emptyset$
|
|||||||
| $t := \text{new} \space Node(k)$ |
$k < t \rightarrow key$
|
$k > t \rightarrow key$
|
$k = t \rightarrow key$
|
||||
| $insert(t \rightarrow left, k)$ | $insert(t \rightarrow right, k)$ | $\text{SKIP}$ | |||||
A minimális kulcsú elem mindig könnyen megtalálható, ha addig követjük a bal oldali pointereket, amíg nullpointert nem találunk.
| $min(t : Node^*) : Node^*$ |
| $t \rightarrow left \neq \emptyset$ | ||||
| $t := t \rightarrow left$ | ||||
| $\text{return} \space t$ | ||||
Először megkeressük a minimális kulcsú elemet. Mivel a minimális kulcsú elemnek nincs bal gyereke, így egyszerűen csak a jobb gyerekét rakjuk a helyére.
| $remMin(\&t, \&minp : Node^*)$ |
|
$t \rightarrow left = \emptyset$
|
||||
| $minp := t$ | $remMin(t \rightarrow left, minp)$ | |||
| $t := minp \rightarrow right$ | ||||
| $minp \rightarrow right := \emptyset$ | ||||
Megváltoztatja a bináris keresőfával ábrázolt dinamikus halmazt. A módosítást természetesen úgy kell végrehajtani, hogy a bináris keresőfa tulajdonság megmaradjon.
A $del()$ megkeresi a fában a törölni kívánt csúcsot, majd meghívja a $delRoot()$-ot.
A $delRoot()$ három esetet vizsgál meg. Ha $t$-nek nincs bal gyereke, akkor egyszerűen a jobb gyerekét kell a helyére rakni. Ha $t$-nek nincs jobb gyereke, akkor egyszerűen a bal gyerekét kell a helyére rakni. Végül, ha a csúcsnak két gyereke van, akkor $t$-nek azt a legközelebbi rákövetkezőjét kell a helyére rakni, melynek már nincs bal oldali gyereke. Ez a jobb oldali részfájának a legkisebb eleme lesz.
| $del(\&t : Node^*, k : \mathcal{T})$ |
|
$t \neq \emptyset$
|
|||||||
|
$k < t \rightarrow key$
|
$k > t \rightarrow key$
|
$k = t \rightarrow key$
|
$\text{SKIP}$ | ||||
| $del(t \rightarrow left, k)$ | $del(t \rightarrow right, k)$ | $delRoot(t)$ | |||||
| $delRoot(\&t : Node^*)$ |
| $p := t$ | |||||
|
$t \rightarrow left = \emptyset$
|
$t \rightarrow right = \emptyset$
|
$t \rightarrow left \neq \emptyset \land t \rightarrow right \neq
\emptyset$
|
|||
| $t := p \rightarrow right$ | $t := p \rightarrow left$ | $remMin(t \rightarrow right, q)$ | |||
| $q \rightarrow left := p \rightarrow left$ | |||||
| $q \rightarrow right := p \rightarrow right$ | |||||
| $t := q$ | |||||
| $\text{delete} \space p$ | |||||
Az aktuális elem inorder bejárás szerinti rákövetkezőjét keressük. Egy elem rákövetkezője a jobb oldali részfájának a legkisebb eleme vagy ha nincs jobb oldali részfája, akkor az az őse, amelytől balra helyezkedik el az elemünk. Ezért a szülő pointerre is szükségünk van. A rákövetkezőt kulcsösszehasonlítások nélkül megtalálhatjuk.
| $inorderNext(p : Node3^*) : Node3^*$ |
| $q := p \rightarrow right$ | ||||
|
$q \neq \emptyset$
|
||||
| $q \rightarrow left \neq \emptyset$ | $q := p \rightarrow parent$ | |||
| $q := q \rightarrow left$ | $q \neq \emptyset \land q \rightarrow left \neq p$ | |||
| $p := q; \space q := q \rightarrow parent$ | ||||
| $\text{return} \space q$ | ||||
Mindegyik műveletre igaz, hogy $MT(h) \in \Theta (h)$, ahol $h$ a fa magassága.
Az alábbi feladatok a gyakorlatokon elvégzendő kötelező, illetve gyakorló feladatok.