5. előadás
Hierarchikus adatszerkezetek A hierarchikus adatszerkezet olyan < A, R > rendezett pár, amelynél van egy kitüntetett r A gyökérelem úgy, hogy: 1. r nem lehet végpont, azaz a A esetén R(a,r) 2. a {A\{r}} elem egyszer és csak egyszer lehet végpont, azaz a {A\{r}}-hez b a, b A: R(b,a)
Hierarchikus adatszerkezetek 3. a {A\{r}} elem r-ből elérhető, azaz a1, a2, an A, an = a : R(r,a1), R(a1, a2) R(an-1, an) A hierarchikus adatszerkezetek valamilyen értelemben a lista általánosításai.
Hierarchikus adatszerkezetek Egy elemnek akárhány rákövetkezője lehet, de minden elemnek csak egyetlen megelőző eleme van, azaz az adatelemek között egy-sok jellegű kapcsolat áll fenn. Minden adatelem csak egy helyről érhető el, de egy adott elemből tetszés szerinti számú adatelem látható. (Pl. fa, összetett lista, B-fa)
Fák A fa egy hierarchikus adatszerkezet, mely véges számú csomópontból áll, és igazak a következők: Két csomópont között a kapcsolat egyirányú, az egyik a kezdőpont, a másik a végpont. Van a fának egy kitüntetett csomópontja, ami nem lehet végpont. Ez a fa gyökere. Az összes többi csomópont pontosan egyszer végpont.
Fák A fa rekurzív definíciója: A fa vagy üres, vagy Van egy kitüntetett csomópontja, ez a gyökér. A gyökérhez 0 vagy több diszjunkt fa kapcsolódik. Ezek a gyökérhez tartozó részfák. A fával kapcsolatos algoritmusok gyakran rekurzívak.
gyökér 0.szint a 1.szint b c részfa levél d e f 3.szint g
Fák Az adatszerkezetben a fa csúcsai az adatelemeknek felelnek meg, az élek az adatelemek egymás utáni sorrendjét határozzák meg - egy csomópontból az azt követőbe húzott vonal egy él. A gyökérelem a fa első eleme, amelynek nincs megelőzője. Levélelem a fa azon eleme, amelynek nincs rákövetkezője. Közbenső elem az összes többi adatelem.
Fák Minden közbenső elem egy részfa gyökereként tekinthető, így a fa részfákra bontható: részfa: t részfája "a" -nak, ha "a" a gyökere, azaz közvetlen megelőző eleme t -nek, vagy t részfája "a" valamely részfájának elágazásszám: közvetlen részfák száma A fa szintje a gyökértől való távolságot mutatja. A gyökérelem a 0. szinten van. A gyökérelem rákövetkezői az 1. szinten. stb. A fa szintjeinek száma a fa magassága.
Fák További definíciók: Csomópont foka: a csomóponthoz kapcsolt részfák száma Fa foka: a fában található legnagyobb fokszám Levél: 0 fokú csomópont Elágazás(közbenső v. átmenő csomópont): >0 fokú csomópont Szülő (ős) : kapcsolat kezdőpontja (csak a levelek nem szülők) Gyerek (leszármazott): kapcsolat végpontja (csak a gyökér nem gyerek) Ugyanazon csomópont leszármazottai egymásnak testvérei Szintszám: gyökértől mért távolság. A gyökér szintszáma 0. Ha egy csomópont szintszáma n, akkor a hozzá kapcsolódó csomópontok szintszáma n+1.
Fák útvonal: az egymást követő élek sorozata Minden levélelem a gyökértől pontosan egy úton érhető el. ág: az az útvonal, amely levélben végződik Üresfa az a fa, amelyiknek egyetlen eleme sincs. ( ) Fa magassága: a levelekhez vezető utak közül a leghosszabb. Mindig eggyel nagyobb, mint a legnagyobb szintszám. Minimális magasságú az a fa, amelynek a magassága az adott elemszám esetén a lehető legkisebb. (Valójában ilyenkor minden szintre a maximális elemszámú elemet építjük be.)
Fák Egy fát kiegyensúlyozottnak nevezünk, ha csomópontjai azonos fokúak, és minden szintjén az egyes részfák magassága nem ingadozik többet egy szintnél. Rendezett fa: ha az egy szülőhöz tartozó részfák sorrendje lényeges, azok rendezettek.
Feladat Maximum hány csomópont helyezhető el egy f fokú, m magasságú fában? f M 1 1 f 1
Fák műveletei Lekérdező Üres_e logikai értéket ad vissza Gyökérelem visszaadja a gyökér adatelemet Keres(e) adott e adatelemet keres, egy ilyen elem mutatóját adja vissza
Fák műveletei Módosító Üres Beszúr(e) MódosítGyökér(e) Töröl(e) TörölFa létrehoz egy üres fát adott e adatelemet beszúr adott e adatelem lesz a gyökér törli az e adatelemet (egy előfordulást összes előfordulást) törli az összes elemet
Fák műveletei Fák bejárása: A fa csomópontjaiban általában adatokat tárolunk. Ezeket valamilyen sorrendben szeretnénk egymás után elérni. Általános fa esetén a bejárási stratégiák: Gyökérkezdő (preorder): gyökér, majd a részfák bejárása sorban (pl. balról jobbra) Gyökérvégző (postorder): részfák bejárása sorban, majd a gyökér
a Preorder bejárás b c a b c d g e f d e f g
a Postorder bejárás b c b g d e f c a d e f g
Bináris fák A bináris fa olyan fa, amelynek csúcspontjaiból maximum 2 részfa nyílik (azaz fokszáma 2). A szülő mindig a gyerekek között helyezkedik el => van értelme a gyökérközepű (inorder) bejárásnak.
Bináris fa a b c d e f g h i j
Bináris fák bejárása A bejárási stratégiák: Gyökérkezdő (preorder): gyökér, bal részfa, jobb részfa Gyökérközepű (inorder): bal részfa, gyökér, jobb részfa Gyökérvégző (postorder): bal részfa, jobb részfa, gyökér
Preorder a jobb részfa gyökér b c d e f bal részfa g h a b d c e g h i j f i j
Inorder a jobb részfa gyökér b c d e f bal részfa g h d b a g e i h j c f i j
Postorder a jobb részfa gyökér b c d e f bal részfa g h d b g i j h e f c a i j
Reprezentáció Általános fa esetén pl. bal-gyermek, jobb-testvér Minden csomóponthoz tartozik három mutató: bal-gyermek a csúcs gyermekei közül a bal szélsőre mutat jobb-testvér a csúcsnak arra a testvérére mutat, amelyik közvetlenül jobbra mellette található (azonos szinten ugyanahhoz az őshöz tartozó következő szomszédos elemre) szülő
Általános fa a b c d e f g
nil a nil Bal-gyermek jobb-testvér nil b c nil d nil e nil f nil nil g nil
Reprezentáció Általános fa esetén például multilista: Minden csomóponthoz tartozik egy lineáris lista, amelynek első eleme az adat, a többi a kapcsolatok listája Annyi kapcsolati elem, ahány fokú a csomópont. A kapcsolatok újabb csomópontokra, illetve lineáris listákra mutatnak.
Általános fa a b c d e f g
a Multilista nil b nil c d nil nil g nil e nil nil f
Reprezentáció Korlátos általános fa esetén pl. aritmetikai ábrázolás is lehetséges láncolt, ahol minden csomópontnak van pontosan k db mutatója a max. k gyerekre
Reprezentáció Bináris fa Aritmetikai ábrázolás: szintfolytonosan egy tömbben: 1 2 3 1 2 3 4 5 6 7 ind(bal(c)) = 2* ind(c) ind(jobb(c)) = 2* ind(c)+1 4 5 6 7
Reprezentáció Bináris fa Láncolt mutató a bal és a jobb gyerekre (esetenként a szülőre is): 1 2 3 4 5 6 7
Bináris fák Egy bináris fa akkor tökéletesen kiegyensúlyozott, ha minden elem bal-, illetve jobboldali részfájában az elemek száma legfeljebb eggyel tér el. Teljesnek nevezünk egy bináris fát, ha minden közbenső elemének pontosan két leágazása van. Majdnem teljes: ha csak a levelek szintjén van esetleg hiány.
Bináris fák Lehetséges műveletek: üres fa inicializálása az üres fa gyökérelemének definiálása a gyökér és a két részfa csoportosítása (az egyik részfa lehet üres) egy elem hozzáadása egy olyan elem bal (jobb) oldalához, amelynek nincs bal (jobb) oldali leágazása jelezzük, ha a fa üres jelezzük, ha nincs bal (jobb) oldali leágazása az aktuális elemnek
Bináris fák Műveletek (folytatás): a gyökérelem elérése egy adott elem elérése, egy elem bal (jobb) oldali részfájának az elérése a gyökérből egy fa kettéválasztása egy elemre (régi gyökér) és egy vagy két részfára (attól függően, hogy a gyökérnek egy vagy két leágazása volt) egy (rész-)fa törlése (a fa lehet egyetlen elem?) egy részfa helyettesítése egy másik részfával
Bináris fák Kiszámítási- vagy kifejezésfa Az a struktúra, amely egy nyelv szimbólumai és különböző műveletei közötti precedenciát jeleníti meg. Aritmetikai kifejezések ábrázolására használják. Minden elágazási pont valamilyen operátort, A levélelemek operandusokat tartalmaznak. A részfák közötti hierarchia fejezi ki az operátorok precedenciáját, illetve a zárójelezést.
Kifejezés fák (a + b) * c - d / e - * / + c d e a b
Példa Legyen adott egy kifejezés lengyel formája az lf sorban, állítsuk elő az f kifejezésfát belőle! a b + + a b
Példa Lengyel forma a b c + * * a + b c
Fa_generál Használjuk a fák vermét! v.empty not lf.isempty e lf.out t operandus(e)? v akt levélgen(e) v.push(t) operátor(e)? jobb v.pop bal v.pop összefűz(bal,e,jobb,t) v.push(t) fa v.pop
összefűz(f1,e,f2,t) levélgen(e) összefűz(,e,,fa) return fa new(t) t.tart e t.jobb f2 t.bal f1
Rendezési (kereső) fák A rendezési fa (vagy keresőfa) olyan bináris fa adatszerkezet, amelynek kialakítása a különböző adatelemek között meglévő rendezési relációt követi. A fa felépítése olyan, hogy minden csúcsra igaz az, hogy a csúcs értéke nagyobb, mint tetszőleges csúcsé a tőle balra lévő leszálló ágon és a csúcs értéke kisebb minden, a tőle jobbra lévő leszálló ágon található csúcs értékénél. A T fa bármely x csúcsára és bal(x) bármely y csúcsára és jobb(x) bármely z csúcsára: y<x<z
Rendezési (kereső) fák A rendezési fa az őt tartalmazó elemek beviteli sorrendjét is visszatükrözi. Ugyanazokból az elemekből különböző rendezési fák építhetők fel.
6,3,1,9,7,5,10 6 3 9 1 5 7 10
9,7,6,5,10,3,1 9 7 10 6 5 3 1
Rendezési (kereső) fák Fontos tulajdonság: inorder bejárással a kulcsok rendezett sorozatát kapjuk. Az algoritmus pszeudokódja: Inorder-fa-bejárás(x) if x NIL then Inorder-fa-bejárás(bal[x]) print(kulcs[x]) Inorder-fa-bejárás(jobb[x]) Egy T bináris keresőfa összes értékének kiíratásához: Inorder-fa-bejárás(gyökér[T])
Rendezési (kereső) fák Az algoritmus helyessége a bináris-kereső-fa tulajdonságból indukcióval adódik. Egy n csúcsú bináris kereső fa bejárása O(n) ideig tart, mivel a kezdőhívás után a fa minden csúcspontja esetében pontosan kétszer (rekurzívan) meghívja önmagát, egyszer a baloldali részfára, egyszer a jobboldali részfára.
Műveletek Keresés: A T fában keressük a k kulcsú elemet (csúcsot); ha ez létezik, akkor visszaadja az elem címét, egyébként NIL-t. Az algoritmust megadjuk rekurzív és iteratív megoldásban is, ez utóbbi a legtöbb számítógépen hatékonyabb.
Műveletek Keresés: A rekurzív algoritmus pszeudokódja: Fában-keres(x, k) if x = NIL or k = kulcs[x] then return x if k < kulcs[x] then return Fában-keres(bal[x], k) else return Fában-keres(jobb[x], k)
Műveletek Keresés: Az iteratív algoritmus pszeudokódja: Fában-iteratívan-keres(x, k) while x NIL and k kulcs[x] do if k < kulcs[x] then x bal[x] else x jobb[x] return x
Műveletek Minimum keresés: tegyük fel, hogy T NIL Addig követjük a baloldali mutatókat, amíg NIL mutatót nem találunk. Az iteratív algoritmus pszeudokódja: Fában-minimum (T) x gyökér[t] while bal[x] NIL do x bal[x] return x Helyessége a bináris-kereső-fa tulajdonságból következik. Lefut O(h) idő alatt, ahol h a fa magassága.
Műveletek Maximum keresés: tegyük fel, hogy T NIL Addig követjük a jobboldali mutatókat, amíg NIL mutatót nem találunk. Az iteratív algoritmus pszeudokódja: Fában-maximum (T) x gyökér[t] while jobb[x] NIL do x jobb[x] return x Helyessége a bináris-kereső-fa tulajdonságból következik. Lefut O(h) idő alatt, ahol h a fa magassága.
Műveletek Következő elem: x csúcs rákövetkezőjét adja vissza, ha van, NIL különben. 10 21 32 if jobb[x] NIL then return Fában-minimum (jobb[x]) 7 12 42 például a 10 rákövetkezője a 11 11 16
Műveletek Következő elem: x csúcs rákövetkezőjét adja vissza, ha van, NIL különben. y szülő[x] while y NIL és x = jobb[y] do x y y szülő[x] return y 7 10 12 21 32 42 16 rákövetkezője a 21 11 16 Nem biztos, hogy gyökér!
Műveletek Következő elem: x csúcs rákövetkezőjét adja vissza, ha van, NIL különben. Fában-következő(T, x) if jobb[x] NIL then return Fában-minimum (jobb[x]) y szülő[x] while y NIL és x = jobb[y] do x y y szülő[x] return y
Műveletek Fában-következő(T, x) futási ideje h magasságú fák esetén O(h). Megelőző elem: x csúcs megelőzőjét adja vissza, ha van, NIL különben. Fában-megelőző(T, x) házi feladat!
Műveletek Tétel: A dinamikus halmazokra vonatkozó Keres, Minimum, Maximum, Következő és Előző műveletek h magasságú bináris keresőfában O(h) idő alatt végezhetők el. Bizonyítás: az előzőekből következik.
Műveletek Beszúrás: A T bináris keresőfába a p csúcsot szúrjuk be. Kezdetben: kulcs[p]=k, bal[p]= NIL, jobb[p]= NIL, szülő[p] = NIL Feltételezzük, hogy a fában még nincs k kulcsú csúcs! (Ha ezt is ellenőrizni kell: házi feladat!)
Műveletek Fába beszúr: szúrjuk be például a 36-t! 1. megkeressük a helyét: 21 < 36 10 32 7 12 42 11 16
Műveletek Fába beszúr: szúrjuk be például a 36-t! 1. megkeressük a helyét: 10 21 32 < 36 7 12 42 11 16
Műveletek Fába beszúr: szúrjuk be például a 36-t! 1. megkeressük a helyét: 21 36 7 10 12 32 42 > a 42 balgyerekének kell befűzni! 11 16
Műveletek Fába beszúr: szúrjuk be például a 36-t! 2. beláncoljuk: 21 10 32 7 12 42 11 16 36
Műveletek Fába-beszúr(T,p) y NIL; x gyökér[t] whilex NIL do y x if kulcs[p] < kulcs[x] then x bal[x] else x jobb[x] szülő[p] y if y = NIL then gyökér[t] p else if kulcs[p] < kulcs[y] then bal[y] p else jobb[y] p megkeressük a helyét beláncoljuk
Műveletek Törlés: A T bináris keresőfából a p csúcsot töröljük. Lehetőségek: 1. p-nek még nincs gyereke: szülőjének mutatóját NIL-re állítjuk 2. p-nek egy gyereke van: a szülője és a gyermeke között építünk ki kapcsolatot 3. p-nek két gyereke van: átszervezzük a fát: kivágjuk azt a legközelebbi rákövetkezőjét, aminek nincs balgyereke, így 1., vagy 2. típusú törlés, majd ennek tartalmát beírjuk p-be.
Műveletek Törlés: töröljük ki például a 11-t! 10 21 32 11-nek még nincs gyereke szülőjének mutatóját NIL-re állítjuk 7 12 42 11 16 36
Műveletek Törlés: töröljük ki például a 12-t! 10 21 32 12-nek egy gyereke van a szülője és a gyermeke között építünk ki kapcsolatot 7 12 42 16 36
Műveletek Törlés: töröljük ki például a 12-t! 10 21 32 12-nek egy gyereke van a szülője és a gyermeke között építünk ki kapcsolatot 7 16 42 36
Műveletek Törlés: töröljük ki például az 5-t! 21 3 6 5 10 12 13 32 36 42 5-nek két gyereke van: átszervezzük a fát: kivágjuk azt a legközelebbi rákövetkezőjét, (aminek nincs balgyereke), így I., vagy II. típusú törlés, majd ennek tartalmát beírjuk az eddigi 5-be (ha lenne balgyereke, akkor nem ez lenne a legközelebbi rákövetkező) 7
Műveletek Törlés: töröljük ki például az 5-t! 21 6 3 5 10 12 13 32 36 42 5-nek két gyereke van: átszervezzük a fát: kivágjuk azt a legközelebbi rákövetkezőjét, (aminek nincs balgyereke), így I., vagy II. típusú törlés, majd ennek tartalmát beírjuk az eddigi 5-be 7
Műveletek Törlés: töröljük ki például az 5-t! 21 6 3 5 10 12 13 32 42 36 5-nek két gyereke van: átszervezzük a fát: kivágjuk azt a legközelebbi rákövetkezőjét, aminek nincs balgyereke, így I., vagy II. típusú törlés, majd ennek tartalmát beírjuk az eddigi 5-be 7
Műveletek Törlés: töröljük ki például az 5-t! 21 3 6 10 12 13 32 42 36 5-nek két gyereke van: átszervezzük a fát: kivágjuk azt a legközelebbi rákövetkezőjét, aminek nincs balgyereke, így I., vagy II. típusú törlés, majd ennek tartalmát beírjuk az eddigi 5-be 7 Az algoritmus pszeudokódja:
Műveletek Fából-töröl (T,p) (feltesszük, hogy p a T-ben egy létező csúcs) if bal[p] = NIL vagy jobb[p] = NIL then y p -- 0 vagy 1 gyerek else y Fában-következő(T, p) -- 2 gyerek if bal[y] NIL then x bal[y] -- x az y 0 vagy 1 gyerekére else x jobb[y] -- mutat if x NIL -- ha volt (egy) gyereke, then szülő[x] szülő[y] -- befűzzük
Műveletek Fából-töröl (T,p) folytatás: if szülő[y] =NIL then gyökér[t ] x -- ha a gyökeret töröltük else if y = bal[szülő[y]] -- y szülőjének megfelelő then bal[szülő[y]] x -- oldali mutatóját else jobb[szülő[y]] x -- x-re állítjuk if y p -- ha a log. törlendő fiz. törlendő then kulcs[p] kulcs[y] -- és a további mezők is return y
Keresések Sok adat esetén milyen adatszerkezetben lehet hatékonyan keresni, módosítani, beszúrni és törölni? A gyakorlat azt mutatja, hogy fákban és táblázatokban.
Keresések Szekvenciális keresések a keresési idő n-nel arányos: O(n) rendezetlen tömbök láncolt listák Bináris keresés rendezett tömbök a keresési idő log 2 n -nel arányos: O(log 2 n)
Keresések Rendezett tömb létrehozása elem hozzáadása megfelelő helyre megkeresem a pozícióját: eltolom: összesen: vagyis: c 1 log 2 n c 2 n c 1 log 2 n + c 2 n c 2 n Minden elem hozzáadása a rendezett tömbhöz: O(n) Nem lehetne-e a rendezett tömböt olcsóbb beszúrásokkal karbantartani? domináns
Keresések Szótár (dictionary) egy adatszerkezet, ha értelmezve vannak a következő műveletek: Keres Beszúr Töröl (Tól-ig)
Keresések Prioritásos sor egy adatszerkezet, ha az előzőeken kívül értelmezve vannak a következők is: Minimum Maximum Előző Rákövetkező Ezzel lehet rendezni
Keresések Adatok a struktúrában: kulcs + mezők (rekordok) Lehetőségek: I. minden kulcs különböző II. lehetnek azonos kulcsok A. rekordok: (k, m1, m2,..mn) B. csak a kulcsokat nézzük: k Mi most az I. és B.-t választjuk.
Bináris keresőfák Mennyire hatékony a bináris keresőfa? Minden művelet egy útvonal bejárását jelenti a fában. A h magasságú fákban O(h) idő alatt hajtódnak végre. Ahogy elemeket beszúrunk és törlünk, változik a fa magassága a műveletek ideje is. Itt ugyanis nem tudom a fa átlagos magasságát. Ha csak beszúrások a felépítés során, könnyebben tudok elemezni:
Bináris keresőfák Legyen adva n különböző kulcs, ebből bináris keresőfát építünk. Ha itt minden sorrend egyformán valószínű, akkor a kapott fát véletlen építésű bináris keresőfának nevezzük. Bizonyítható, hogy egy n kulcsból véletlen módon épített bináris keresőfa átlagos magassága O(log 2 n).
Bináris keresőfák Tegyük fel, hogy a véletlen sorrendű 1,2, n adatokból építjük fel a t keresőfát. Mennyi az átlagos csúcsmagasság? = Hány összehasonlítással lehet felépíteni a t keresőfát átlagosan? Építsünk keresőfát
Bináris keresőfák 5
0 5 Bináris keresőfák 2
1 2 0 5 Bináris keresőfák 9
0 Bináris keresőfák 5 3 1 2 1 9
0 Bináris keresőfák 5 11 1 2 1 9 2 3
0 Bináris keresőfák 5 1 1 2 1 9 2 2 3 11
0 Bináris keresőfák 5 6 1 2 1 9 2 2 2 1 3 11
0 Bináris keresőfák 5 10 1 2 1 9 2 2 2 2 1 3 6 11
0 Bináris keresőfák 5 8 1 2 1 9 2 2 2 2 1 3 6 11 10 3
0 Bináris keresőfák 5 7 1 2 1 9 2 2 2 2 1 3 6 11 3 8 10 3
0 Bináris keresőfák 5 4 1 2 1 9 2 2 2 2 1 3 6 11 3 8 10 3 4 7
0 5 Bináris keresőfák 1 2 1 9 2 2 2 2 1 3 6 11 3 4 3 8 10 3 4 7
0 Bináris keresőfák 5 2 1 1 2 2 3 1 2 9 6 2 11 p= 5,2,9,3,11,1,6,10,8,7,4 esetén: Ö(p)= (1+1) + (2+2+2+2)+ (3+3+3)+4=23 3 4 3 8 10 3 4 7
Bináris keresőfák Határozzuk meg ennek az átlagát! Jelölés: f(n): n adatból hány összehasonlítással lehet keresőfát építeni f(n k) : először a k érték jön (k az input sorozat első eleme) Tegyük fel, hogy minden sorozat egyforma valószínűségű.
Bináris keresőfák 1 n f ( n) f ( n k) n k 1
Bináris keresőfák k-1 k n-k összehasonlítás f(k-1) f(n-k) k-1 n-k
Bináris keresőfák
Bináris keresőfák Belátható, hogy f(n) < 2n * ln n 1,39 n log 2 n Tehát a fa átlagos csúcsmagassága 1,39 log 2 n
Bináris keresőfák A sorrend megőrzése fontos ez a transzformáció megőrzi a keresőfát:
Bináris keresőfák A sorrend megőrzése fontos ez a transzformáció megőrzi a keresőfát: Egy forgatást hajtottunk végre a T és O csúcsok körül.
Bináris keresőfák A forgatások lehetnek bal- vagy jobb-forgatások Mindkét fára az inorder bejárás: A x B y C jobbra-forgat balra-forgat
Bináris keresőfák A forgatások lehetnek bal- vagy jobb-forgatások Mindkét fára az inorder bejárás: A x B y C jobbra-forgat balra-forgat Ebben a forgatásban, a B-t az x jobb gyerekéből át kellett mozgatni az y bal gyerekének!