Algoritmusok és adatszerkezetek II. el adásjegyzet (részletek) Ásványi Tibor asvanyi@inf.elte.u 19. február 4.
Tartalomjegyzék 1. Bevezetés, Ajánlott irodalom 3. Tematika 6 3. AVL fák 8 3.1. AVL fák: beszúrás........................ 1 3.. AVL fák: a remmin(t, minp) eljárás............... 18 3.3. AVL fák: törlés.......................... 3.4. Az AVL fák magassága*..................... 1 4. Általános fák 4. B+ fák és m veleteik 6
1. Bevezetés, Ajánlott irodalom Kedves Hallgatók! A vizsgára való készülésben els sorban az el adásokon és a gyakorlatokon készített jegyzeteikre támaszkodatnak. További ajánlott források: Hivatkozások [1] Ásványi Tibor, Algoritmusok és adatszerkezetek II. Útmutatások a tanulásoz, Tematika, AVL fák (19) ttp://aszt.inf.elte.u/ asvanyi/ad/adjegyzet.pdf [] Ásványi Tibor, Algoritmusok II. gyakorló feladatok (16) ttp://aszt.inf.elte.u/ asvanyi/ad/adfeladatok.pdf [3] Cormen, T.H., Leiserson, C.E., Rivest, R.L., Stein, C., magyarul: Új Algoritmusok, Scolar Kiadó, Budapest, 3. ISBN 963 9193 9 9 angolul: Introduction to Algoritms (Tird Edititon), Te MIT Press, 9. [4] Fekete István, Algoritmusok jegyzet ttp://ifekete.web.elte.u/ [] Koruely Gábor, Szalay Ricárd, Algoritmusok és adatszerkezetek, 1/16 tavaszi félév (allgatói jegyzet, lektorált és javított) ttp://aszt.inf.elte.u/ asvanyi/ad/ [6] Rónyai Lajos Ivanyos Gábor Szabó Réka, Algoritmusok, T ypot E X Kiadó, 1999. ISBN 963 913 16 [7] Tarjan, Robert Endre, Data Structures and Network Algoritms, CBMS-NSF Regional Conference Series in Applied Matematics, 1987. [8] Weiss, Mark Allen, Data Structures and Algoritm Analysis, Addison-Wesley, 199, 1997, 7, 1, 13. [9] Carl Burc, Ásványi Tibor, B+ fák ttp://aszt.inf.elte.u/ asvanyi/ad/b+ fa.pdf 3
[1] Ásványi Tibor, Algoritmusok és adatszerkezetek I. el adásjegyzet (17) ttp://aszt.inf.elte.u/ asvanyi/ad/ad1jegyzet.pdf [11] Wirt, N., Algoritms and Data Structures, Prentice-Hall Inc., 1976, 198, 4. magyarul: Algoritmusok + Adatstruktúrák = Programok, M szaki Könyvkiadó, Budapest, 198. ISBN 963 1 388 Az itt következ el adásjegyzetekben bizonyos fejezetek még nem teljesek; egyel re csak az AVL fákkal kapcsolatos részeket dolgoztam ki részletesen, az általános fákat részben, illetve a B+ fák témakörben angol nyelv egyetemi tananyag fordítását bocsátom rendelkezésükre. Nagy segítség leet még a felkészülésben a fent ivatkozott allgatói jegyzet is []. Az el adásokon tárgyalt programok struktogramjait igyekeztem minden esetben megadni, a másolási ibák kiküszöbölése érdekében. E tananyagon kívül a megértést segít ábrák találatóak b ségesen az ajánlott segédanyagokban. Ezúton szeretnék köszönetet mondani Umann Kristófnak az ebben a jegyzetben találató szép, színvonalas szemléltet ábrák elkészítéséért, az ezekre szánt id ért és szellemi ráfordításért! A vizsgákon az elméleti kérdések egy-egy tétel bizonyos részleteire vonatkoznak. Lesznek még megoldandó feladatok, amik részben a tanult algoritmusok m ködésének szemléltetését, bemutatását, részben a szerzett ismeretek kreatív felasználását kérik számon. Egy algoritmus, program, m velet bemutatásának mindig része a m veletigény elemzése. Az el adások els sorban a CLRS könyv [3] (ld. alább) angol eredetijének armadik kiadását követik, de pl. a piros-fekete fák elyett az AVL fákat tárgyaljuk. (A CLRS könyv [3] érintett fejezeteiben a magyar és az angol változat között leginkább csak néány jelölésbeli különbséget találtunk.) Az egyes struktogramokat általában nem dolgozzuk ki az értékadó utasítások szintjéig. Az olyan implementációs részleteket, mint a listák és egyéb adatszerkezetek, adattípusok m veleteinek pontos kódja, a dinamikusan allokált objektumok deallokálása stb. általában az Olvasóra agyjuk, iszen ezekkel az el z félévben foglalkoztunk. Használni fogunk olyan absztrakt fogalmakat, mint a véges almazok, sorozatok, gráfok. Ezeket, a valamely eljárás paraméterlistáján szerepelnek mint strukturált adatokat minden esetben cím szerint vesszük át. (A skalárok változatlanul érték vagy cím szerint adódnak át, aol az érték szerinti paraméterátvétel az alapértelmezett.) A struktogramokban a for ciklusok (illetve a for eac ciklusok) mintájára alkalmazni fogunk a ciklusfeltételek elyén pl. x : P (x) alakú 4
kifejezéseket, ami azt jelenti, ogy a ciklusmagot a P (x) állítás igazsáalmazának minden x elemére végre kell ajtani, valamint v V alakúakat, ami a v : v V rövidítése. Eez P (x) igazságalmazának, illetve V -nek végesnek, és atékonyan felsorolatónak kell lennie. Ez ideális esetben azt jelenti, ogy a felsorolás m veletigénye az igazságalmaz, illetve V méretét l lineárisan függ. Ha a ciklus fejében i = u to v alakú kifejezést látunk, akkor a ciklusmag az u..v egész intervallum i elemeire szigorúan monoton növekv sorrendben ajtódik végre. Ha pedig a ciklus fejében i = u downto v alakú kifejezést látunk, akkor a ciklusmag a v..u egész intervallum i elemeire ajtódik végre, de szigorúan monoton csökken sorrendben. A fentiek szerint az egyszer bb programrészletek elyén gyakran szerepelnek majd magyar nyelv utasítások, amiknek részletes átgondolását, esetleges kidolgozását, a korábban tanultak alapján, szintén az Olvasóra bízzuk. A struktogramokban az ilyen, formailag általában felszólító mondatok végér l a felkiáltójelet elagyjuk (mivel az adott szövegkörnyezetben ez gyakran faktoriális függvényként lenne [félre]értet ).
. Tematika Minden tételez: Hivatkozások: például a [3] 8., 8.3, 8.4 jelentése: a [3] sorszámú szakirodalom adott (al)fejezetei. 1. Veszteségmentes adattömörítés. Naiv módszer. Human kód, kódfa, optimalitás. LZW (Lempel-Ziv-Welc) tömörítés. []. Általános fák, bináris láncolt reprezentáció, bejárások ([1]; [11] 4.7 bevezetése). Egyéb reprezentációk? (HF) 3. AVL fák és m veleteik: kiegyensúlyozási sémák, programok. Az AVL fa magassága ([1], [4] 1; [11] 4.). 4. B+ fák és m veleteik [9].. Elemi gráf algoritmusok ([3] ). Gráf ábrázolások (representations of graps). A szélességi gráfkeresés (breadt-rst searc: BFS). A szélességi gráfkeresés futási ideje (te run-time analysis of BFS). A legrövidebb utak (sortest pats). A szélességi feszít fa (breadt-rst tree). HF: A szélességi gráfkeresés megvalósítása a klasszikus gráf ábrázolások esetén; atékonyság. A mélységi gráfkeresés (dept-rst searc: DFS). Mélységi feszít erd (dept-rst forest). A gráf csúcsainak szín és id pont címkéi (colors and timestamps of vertexes). Az élek osztályozása (classication of edges, its connections wit te colors and timestamps of te vertexes). A mélységi gráfkeresés futási ideje (te run-time analysis of DFS). Topologikus rendezés (topological sort). HF: A mélységi gráfkeresés és a topologikus rendezés megvalósítása a klasszikus gráf ábrázolások esetén; atékonyság. 6. Minimális feszít fák (Minimum Spanning Trees: MSTs). Egy általános algoritmus (A general algoritm). Egy tétel a biztonságos élekr l és a minimális feszít fákról (A teorem on safe edges and MSTs). Prim és Kruskal algoritmusai (Te algoritms of Kruskal and Prim). A futási id k elemzése (Teir run-time analysis). HF: A Prim algoritmus implementációja a két f gráfábrázolás és a szükséges prioritásos sor különböz megvalósításai esetén (Te implementations of te algoritm of Prim wit respect to te main grap representations and representations of te priority queue). 7. Legrövidebb utak egy forrásból (Single-Source Sortest Pats). A legrövidebb utak fája (Sortest-pats tree). Negatív körök (Negative cycles). Közelítés (Relaxation). 6
A sor-alapú (Queue-based) Bellman-Ford algoritmus. A menet (pass) fogalma. Futási id elemzése. Helyessége. A legrövidebb út kinyomtatása. Legrövidebb utak egy forrásból, körmentes irányított gráfokra. (DAG sortest pats.) Futási id elemzése. Helyessége. Dikstra algoritmusa. Helyessége. Fontosabb implementációi a két f gráfábrázolás és a szükséges prioritásos sor különböz megvalósításai esetén. A futási id k elemzése. 8. Legrövidebb utak minden csúcspárra (All-Pairs Sortest Pats). A megoldás ábrázolása a (D, Π) mátrix-párral. HF: Adott csúcspárra a legrövidebb út kinyomtatása. A Floyd-Warsall algoritmus és a (D (k), Π (k) ) mátrix párok. A futási id elemzése. Összeasonlítás a Dijkstra algoritmus, illetve (HF:) a sor-alapú Bellman-Ford algoritmus G.V -szeri végreajtásával. Irányított gráf tranzitív lezártja (Transitive closure of a directed grap) és a T (k) mátrixok. Az algoritmus és futási ideje. HF: összeasonlítás a szélességi keresés G.V -szeri végreajtásával. 9. Mintaillesztés (String Matcing). Egy egyszer mintailleszt algoritmus (Te naive string-matcing algoritm). A futási id elemzése. A Rabin-Karp algoritmus. Inicializálása. A futási id elemzése. A Knut-Morris-Pratt algoritmus. Inicializálása. A futási id elemzése. A Quick Searc algoritmus. Inicializálása. A futási id elemzése. 7
3. AVL fák Az AVL fák kiegyensúlyozásának szabályai megtalálatók az 1-6. ábrákon. Az AVL fák magasság szerint kiegyensúlyozott bináris keres fák. Egy bináris fa magasság szerint kiegyensúlyozott, a minden csúcsa kiegyensúlyozott. (Mostantól kiegyensúlyozott alatt magasság szerint kiegyensúlyozottat értünk.) Egy bináris fa egy ( p) csúcsa kiegyensúlyozott, a a csúcs (p b) egyensúlyára (balance) p b 1. A ( p) csúcs egyensúlya deníció szerint: p b = (t rigt) (t left) Az AVL fákat láncoltan reprezentáljuk. A csúcsokban a b egyensúly attribútumot expliciten tároljuk. (Egy másik leet ség a két részfa magasságainak tárolása lenne; egy armadik, ogy csak az aktuális részfa magasságát tároljuk.) A Node osztályt teát a következ képpen módosítjuk: Node + key : T // T is some known type + b : 1..1 // te balance of te node + left, rigt : Node* + Node() { left = rigt = ; b = } // create a tree of a single node + Node(x:T) { left = rigt = ; b = ; key = x } Egyel re részletes bizonyítás nélkül közöljük az alábbi eredményt: Tétel: Tetsz leges n csúcsú nemüres AVL fa magasságára: lg n 1, 4 lg n, azaz Θ(lg n) A bizonyítás vázlata: El ször a magasságú, nemüres KBF-ek (kiegyensúlyozott, bináris fák) n méretére adunk alsó és fels becslést. Az n < +1 becslésb l azonnal adódik lg n. Másrészt megatározzuk a mélység, legkisebb méret KBF-ek csúcsainak f számát. Erre kapjuk, ogy f = 1, f 1 =, f = 1 + f 1 + f ( ). Ezért az ilyen fákat Fibonacci fáknak ívjuk. Mivel tetsz leges magasságú KBF n méretére n f, némi matematikai ügyességgel kapatjuk a 1, 4 lg n egyenl tlenséget. Mivel az AVL fák magassága Θ(lg n), ezért a bináris keres fák searc(t, k), min(t) és max(t) függvényeire t AVL fa esetén automatikusan MT (n) Θ(lg n), aol n = t. 8
t t (t) = + 3 α T ++ β R + γ + 1 L R T α β γ + 1 (t) = + 1. ábra. (++, +) forgatás. t t (t) = + 3 α + 1 L - β T -- γ R α + 1 L β T γ (t) = +. ábra. (, ) forgatás. 9
t t (t) = + 3 α T β 1 ++ L R - + γ 1 - δ RL - α T β 1 L γ 1 R + δ (t) = + 3. ábra. (++, ) forgatás. t t (t) = + 3 α L β 1 + R T - + γ 1 -- δ LR - α L β 1 R γ 1 T + δ (t) = + 4. ábra. (, +) forgatás. 1
t t T -- L + (t) = + 3 L α β + 1 + 1 γ R α + 1 T β + 1 - γ (t) = + 3. ábra. (, ) forgatás. t t T ++ R - (t) = + 3 α R β γ + 1 + 1 L α T + β + 1 γ + 1 (t) = + 3 6. ábra. (++, ) forgatás. 11
Az insert(t, k), a del(t, k), a remmin(t, minp) és a remmax(t, maxp) eljárások azonban változtatják a fa alakját. Így elromolat a kiegyensúlyozottság, és már nem garantált a fenti m veletigény. Ennek elkerülésére ezeket az eljárásokat úgy módosítjuk, ogy minden egyes rekurzív eljárásívás után ellen rizni fogjuk, ogyan változott a megfelel részfa magassága, és ez ogyan befolyásolta a felette lev csúcs kiegyensúlyozottságát, szükség esetén elyreállítva azt. Ez minden szinten legfeljebb konstans mennyiség extra m veletet fog jelenteni, és így a kiegészített eljárások futási ideje megtartja az MT (n) Θ(lg n) (aol n = t ) nagyságrendet. A példákoz a (bal_részfa gyökér jobb_részfa) jelölést vezetjük be, aol az üres részfákat elagyjuk, és a könnyebb olvasatóság kedvéért [] és {} zárójeleket is alkalmazunk. Például a {[]4[(6)8(1)]} bináris keres fa gyökere a 4; bal részfája a [], ami egyetlen levélcsúcsból (és annak üres bal és jobb részfáiból) áll; jobb részfája a [(6)8(1)], aminek gyökere a 8, bal részfája a (6), jobb részfája a (1). Az így ábrázolt fák inorder bejárása a zárójelek elagyásával adódik. A bels csúcsok egyensúlyait kb formában jelöljük, aol k a csúcsot azonosító kulcs, b pedig a csúcs egyensúlya, a :, 1:+, :++, -1:, -: megfeleltetéssel. Mivel a levélcsúcsok egyensúlya mindig nulla, a leveleknél nem jelöljük az egyensúlyt. A fenti AVL fa például a megfelel egyensúlyokkal a következ : {[]4+[(6)8 (1)]} Az AVL fák m veletei során a kiegyensúlyozatlan részfák kiegyensúlyozásáoz forgatásokat fogunk asználni. Az alábbi forgatási sémákban görög kisbet k jelölik a részfákat (amelyek minden esetben AVL fák lesznek), latin nagybet k pedig a csúcsok kulcsait (1. és. ábrák, az egyensúlyok nélkül): Balra forgatás (Left rotation): [α T (β R γ)] [(α T β) R γ] Jobbra forgatás (Rigt rotation): [(α L β) T γ] [α L (β T γ)] Vegyük észre, ogy a fa inorder bejárását egyik forgatás sem változtatja, így a bináris keres fa tulajdonságot is megtartják. Mint látni fogjuk, a kiegyensúlyozatlan részfák kiegyensúlyozása minden esetben egy vagy két forgatásból áll. Ezért a bináris keres fa tulajdonságot a kiegyensúlyozások is megtartják. 3.1. AVL fák: beszúrás Nézzük most el ször a bináris keres fák insert(t, k) eljárását! A {[]4+[(6)8 (1)]} AVL fából az 1 beszúrásával az {[(1) ]4 [(6)8 (1)]} AVL fa adódik, a 3 beszúrásával pedig az {[+(3)]4 [(6)8 (1)]} AVL fa. 1
A fenti {[]4+[(6)8 (1)]} AVL fából a 7 beszúrásával azonban a {[]4++[(6+{7})8 (1)]} fát kapjuk, a 9 beszúrásával pedig a {[]4++[(6)8+({9}1 )]} fát. Mindkett bináris keres fa, de már nem AVL fa, mivel a 4++ csúcs nem kiegyensúlyozott. A legutolsó esetben a bináris keres fa könnyen kiegyensúlyozató, a meggondoljuk, ogy az a következ sémára illeszkedik, amiben görög kisbet k jelölik a kiegyensúlyozott részfákat, latin nagybet k pedig a csúcsok kulcsait: [α T++ (β R+ γ)], aol T=4, α=[], R=8, β=(6) és γ=({9}1 ). A kiegyensúlyozásoz szükséges transzformáció a fa balra forgatása (1. ábra): [ α T++ (β R+ γ) ] [ (α T β) R γ ] A fenti példán ez a következ t jelenti: {[]4++[(6)8+({9}1 )]} { [()4 (6)] 8 [(9)1 ] } A transzformáció elyességének belátásáoz bevezetjük a = (α) jelölést. Ebb l a kiinduló fára a T++ és R+ egyensúlyok miatt ((β R γ)) = +, (γ) = +1 és (β) = adódik, innét pedig az eredmény fára (α) = = (β) miatt T ; továbbá ((α T β)) = + 1 = (γ) miatt R adódik. Az eredeti {[]4+[(6)8 (1)]} AVL fába a 7 beszúrásával adódó { []4++[(6+{7})8 (1)] } kiegyensúlyozatlan bináris keres fa kiegyensúlyozásával pedig {[()4 ] 6 [(7)8 (1)]} adódik, amire az { α T++ [(β L + γ) R δ] } { [α T β] L [γ R+ δ] } séma (3. ábra) szolgál, aol α, β, γ és δ AVL fák, és a árom jelb l álló egyensúlyok sorban árom esetet jelentenek úgy, ogy a bal oldalon az i-edik alternatívának a jobb oldalon is az i-edik eset felel meg (i {1; ; 3}). A fenti séma a példában T=4, α = [], R=8, δ = (1), L=6, + egyensúllyal (armadik eset), β = és γ = {7} elyettesítéssel alkalmazató. A transzformációt kett s forgatásnak is tekintetjük: Az { α T [(β L γ) R δ] } fára el ször az R csúcsnál alkalmaztunk egy jobbra forgatást, aminek eredményeképpen az { α T [β L (γ R δ)] } bináris keres fát kaptuk, majd az eredmény fát balra forgattuk, ami az { [α T β] L [γ R δ] } AVL fát eredményezte. A kett s forgatás elyességének ellen rzéséez kiszámítjuk az eredmény fa egyensúlyait. Most is bevezetjük a = (α) jelölést. Mivel a kett s forgatás 13
el tti fában T++, azért ([(β L γ) R δ]) = +. Innét R miatt ((β L γ)) = +1 és (δ) =. Most L leetséges egyensúlyai szerint árom eset van. (1) L esetén (β) = és (γ) = 1 az eredmény fában T és R+. () L esetén (β) = és (γ) = az eredmény fában T és R. (3) L+ esetén (β) = 1 és (γ) = az eredmény fában T és R. Mindárom esetben ([α T β]) = + 1 = ([γ R δ]), így L. Ezzel beláttuk a kett s forgatási séma elyességét. Vegyük észre, ogy a fentiek alapján, az L csúcsnak a kett s forgatás el tti egyensúlyát s-sel, a T és a R csúcsoknak pedig a kett s forgatás utáni egyensúlyát rendre s t -vel, illetve s r -vel jelölve a következ összefüggéseket íratjuk fel: s t = (s + 1)/ és s r = (1 s)/ Az eddigi két kiegyensúlyozási séma mellett természetes módon adódnak ezek tükörképei, a forgatások el tt a T csúccsal a gyökérben. Ezek teát a következ k (. és 4. ábra): [ (α L β) T γ ] [ α L (β T γ) ] { [α L+ (β R + γ)] T δ } [ (α L β) R (γ T+ δ) ] s l = (s + 1)/ és s t = (1 s)/ aol s az R csúcs kett s forgatás el tti egyensúlya; s l, illetve s t pedig rendre az L és T csúcsoknak a kett s forgatás utáni egyensúlya Eddig nem beszéltünk arról, ogy az AVL fában az új csúcs beszúrása után mely csúcsoknak és milyen sorrendben számoljuk újra az egyensúlyát, sem ogy mikor ütemezzük be a kiegyensúlyozást. Csak a beszúrás nyomvonalán visszafelé aladva kell újraszámolnunk az egyensúlyokat, és csak itt kell kiegyensúlyoznunk, a kiegyensúlyozatlan csúcsot találunk. Így a futási id a fa magasságával arányos marad, ami AVL fákra O(lg n). Most teát részletezzük a beszúró és kiegyensúlyozó algoritmus m ködését. Ennek során a fa magassága vagy eggyel növekszik (d = true), vagy ugyanannyi marad (d = false). 14
t = new Node(k) d = true AVLinsert( &t:node* ; k:t ; &d:b ) k < t key AVLinsert(t left, k, d) d leftsubtreegrown (t, d) SKIP t == k > t key AVLinsert(t rigt, k, d) d rigtsubtreegrown (t, d) leftsubtreegrown( &t:node* ; &d:b ) l = t left t b == 1 l b == 1 balancemmm(t, l) balancemmp(t, l) d = false SKIP t b = t b 1 d = (t b < ) ELSE d = amis rigtsubtreegrown( &t:node* ; &d:b ) t b == 1 r = t rigt t b = t b + 1 r b == 1 balanceppp(t, r) balanceppm(t, r) d = (t b > ) d = false balanceppp( &t, r : Node* ) t rigt = r left r left = t r b = t b = t = r balancemmm( &t, l : Node* ) t left = l rigt l rigt = t l b = t b = t = l 1
balanceppm( &t, r : Node* ) l = r left t rigt = l left r left = l rigt l left = t l rigt = r t b = (l b + 1)/ r b = (1 l b)/ l b = t = l balancemmp( &t, l : Node* ) r = b rigt l rigt = r left t left = r rigt r left = l r rigt = t l b = (r b + 1)/ t b = (1 r b)/ r b = t = r Az AVL fába való beszúrást röviden összefoglalva: 1. Megkeressük a kulcs elyét a fában.. Ha a kulcs benne van a fában, KÉSZ vagyunk. 3. Ha a kulcs elyén egy üres részfa találató, beszúrunk az üres fa elyére egy új, a kulcsot tartalmazó levélcsúcsot, azzal, ogy ez a részfa eggyel magasabb lett. 4. egyet fölfelé lépünk a keres fában. Mivel az a részfa, amib l fölfele léptünk, eggyel magasabb lett, az aktuális csúcs egyensúlyát megfelel képp módosítjuk. (Ha a jobb részfa lett magasabb, ozzáadunk az egyensúlyoz egyet, a a bal, levonunk bel le egyet.). Ha az aktuális csúcs egyensúlya lett, akkor az aktuális csúcsoz tartozó részfa alacsonyabb ága ozzán tt a magasabbikoz, teát az aktuális részfa most ugyanolyan magas, mint a beszúrás el tt volt, és így egyetlen más csúcs egyensúlyát sem kell módosítani: KÉSZ vagyunk. 6. Ha az aktuális csúcs új egyensúlya 1 vagy -1, akkor el tte volt, ezért az aktuális részfa magasabb lett eggyel. Ekkor a 4. ponttól folytatjuk. 7. Ha az aktuális csúcs új egyensúlya vagy -, 1 akkor a ozzá tartozó részfát ki kell egyensúlyozni. A kiegyensúlyozás után az aktuális részfa visszanyeri a beszúrás el tti magasságát, ezért már egyetlen más csúcs egyensúlyát sem kell módosítani: KÉSZ vagyunk. Az az állítás, ogy ebben az algoritmusban a kiegyensúlyozás után az aktuális részfa visszanyeri a beszúrás el tti magasságát, még igazolásra vár. Azt az esetet nézzük meg, amikor a kiegyensúlyozandó részfa gyökere T++. A T 1 A és - eseteket a struktogramban nem számoltuk ki expliciten, ogy az egyensúly tárolására elég legyen két bit. 16
eset asonlóan fontolató meg. sémák (1. és 3. ábra): A T++ esetez tartozó kiegyensúlyozási [ α T++ (β R+ γ) ] [(α T β) R γ] { α T++ [(β L + γ) R δ] } { [α T β] L [γ R+ δ] } El ször belátjuk, ogy a a T csúcs jobboldali R gyereke + vagy súlyú, akkor a fenti sémák közül a megfelel alkalmazató: Mivel a beszúró algoritmus a fában a beszúrás elyét l egyesével fölfele lépked, és az els kiegyensúlyozatlan csúcsnál azonnal kiegyensúlyoz, ez alatt nincs kiegyensúlyozatlan csúcs, azaz az α, β és γ, illetve a δ részfák is kiegyensúlyozottak, ez pedig éppen a fenti forgatások feltétele, amellett, ogy bináris keres fát akarunk kiegyensúlyozni, amit viszont a kiegyensúlyozás nélküli beszúró algoritmus garantál. Most még be kell látni, ogy a fenti sémák minden esetet lefednek, azaz R+ vagy R : egyrészt, R nem leet a beszúrás által létreozott új csúcs, mert különben T-nek a beszúrás el tti jobboldali részfája üres lett volna, teát most nem leetne T++. Másrészt, a a fölfele lépkedés során nulla egyensúlyú csúcs áll el, akkor a fölötte lev csúcsok egyensúlya már nem módosul, így kiegyensúlyozatlan csúcs sem állat el. Márpedig most T++. Így teát az új csúcstól T-ig fölfelé vezet úton minden csúcs, azaz R egyensúlya is + vagy. Most belátjuk, ogy a kiegyensúlyozások visszaállítják a részfa beszúrás el tti magasságát. A T++ kiegyensúlyozatlan csúcs a beszúrás el tt kiegyensúlyozott volt. Mivel a beszúrás, a beszúrás elyét l kezdve T-ig fölfelé vezet úton mindegyik részfa magasságát pontosan eggyel növelte, a beszúrás el tt T+ volt. Ezért a beszúrás el tt, = (α) jelöléssel, a T gyöker részfa + magas volt. A beszúrás után teát T++ lett, és így a T gyöker részfa + 3 magas lett. A beszúrás utáni, de még a kiegyensúlyozás el tti állapotot tekintve a továbbiakban megkülönböztetjük a T jobboldali R gyerekére a R+ és a R eseteket. R+ esetén már láttuk, ogy (α) = = (β) és (γ) = + 1. Ezért a kiegyensúlyozás után ([(α T β) R γ]) = +. R esetén pedig már láttuk, ogy (α) = = (δ) és ([β L γ]) = + 1. Ezért (β), (γ). Így a kiegyensúlyozás után ({[α T β] L [γ R δ]}) = +. Ezzel beláttuk, ogy a kiegyensúlyozások mindkét esetben visszaállítják a részfa beszúrás el tti magasságát, mégpedig úgy, ogy a beszúrás által eggyel megnövelt magasságot eggyel csökkentik. Szimmetria okokból ez asonlóan látató be T esetén is, gyelembe véve a L és a L+ eseteket. 17
3.. AVL fák: a remmin(t, minp) eljárás Bináris keres fákra a remmin eljárás: remmin( &t, &minp : Node* ) t left == minp = t t = minp rigt remmin(t left, minp) minp rigt = Ezt például a { []4+[(6)8 (1)] } AVL fára alkalmazva, a minp key = eredmény mellett, a { 4++[(6)8 (1)] } kiegyensúlyozatlan bináris keres fa adódna. Látjuk, ogy a kiegyensúlyozatlan 4++ csúcs jobboldali gyereke a 8. Ennek az esetnek a kezelésére új kiegyensúlyozási sémát kell alkalmaznunk. Szerencsére egy balra forgatás, a megfelel egyensúly beállítások mellett most is megoldja a problémát (6. ábra): { α T++[β R γ] } { [α T+ β] R γ }. T++ miatt = (α) jelöléssel nyilván (β) = + 1 = (γ), így a forgatás után az egyensúlyokat a fenti séma szerint kell beállítani. A fa magassága a forgatás el tt és után is + 3 lesz, ez a fajta kiegyensúlyozás teát az eddigiekkel szemben nem csökkenti az aktuális részfa magasságát. Ezután az AVL fákra alkalmazott, a megfelel kiegyensúlyozásokkal kiegészített AVLremMin eljárás pl. a következ leet (d most azt jelöli, ogy csökkent-e az aktuális részfa magassága): AVLremMin( &t, &minp:node* ; &d:b ) t left == minp = t t = minp rigt minp rigt = d = true AVLremMin(t left, minp, d) d leftsubtreesrunk(t, d) SKIP 18
leftsubtreesrunk( &t:node* ; &d:b ) t b == 1 balance_pp(t, d) t b = t b + 1 d = (t b = ) r b == 1 balanceppm(&t, r) balance_pp( &t:node* ; &d:b ) r = t rigt r b == balancepp(&t, r) d = false r b == 1 balanceppp(&t, r) balancepp( &t, r : Node* ) t rigt = r left r left = t t b = 1 r b = 1 t = r Az algoritmus elyessége asonlóan gondolató meg, mint a beszúrás esetén. Lényeges különbség azonban, ogy ott minden beszúrást csak egyetlen kiegyensúlyozás követ, míg itt el fordulat, ogy a minimális csúcs eltávolítása után, minden felette lév szinten ki kell egyensúlyozni. 3.1. Feladat. Mutassunk ilyen, legalább négy magasságú fát! 3.. Feladat. Írjuk meg az AVLremMin(t, minp, d) eljárás mintájára az AVLremMax(t, maxp, d) eljárást és segédeljárásait! 19
3.3. AVL fák: törlés Bináris keres fákra a del(t, k) és a delroot(t) eljárások: k < t key del(t left, k) del( &t:node* ; k:t ) t k > t key k == t key del(t rigt, k) delroot(&t) SKIP t left == p = t t = p rigt delete p delroot( &t:node* ) t rigt == p = t t = p left delete p t left t rigt remmin(t rigt, p) p left = t left p rigt = t rigt delete t ; t = p Ezeket fogjuk most az AVLremMin(&t, &minp, &d) eljárás mintájára kiegészíteni a d paraméterrel; majd a rekurzív töröl ívásokból, valamint az AVLremMin eljárásból való visszatérés után megkérdezzük, csökkent-e az aktuális részfa magassága. Ha igen, az AVLremMin(&t, &minp, &d) eljárás mintájára módosítjuk a t b értéket, a kell, kiegyensúlyozunk, és a kell, d-t beállítjuk. k < t key AVLdel(t left, k, d) d leftsubtreesrunk (t, d) AVLdel( &t:node* ; k:t ; &d:b ) SKIP t k > t key AVLdel(t rigt, k, d) d rigtsubtreesrunk (t, d) SKIP k == t key AVLdelRoot (t, d) d = amis
t left == p = t t = p rigt delete p d = true AVLdelRoot( &t:node* ; &d:b ) t rigt == p = t t = p left delete p d = true t left t rigt rigtsubtreemintoroot(t, d) d rigtsubtreesrunk(t, d) rigtsubtreemintoroot( &t:node* ; &d:b ) AVLremMin(t rigt, p, d) p left = t left ; p rigt = t rigt ; p b = t b delete t ; t = p SKIP Itt is leetséges, ogy több szinten, a legrosszabb esetben akár minden szinten is ki kell egyensúlyozni. Mivel azonban egyetlen kiegyensúlyozás sem tartalmaz se rekurziót, se ciklust, és ezért konstans számú eljárásívásból áll, ez sem itt, sem az AVLremMin eljárásnál nem befolyásolja a futási id nek az AVLinsert eljárásra is érvényes MT (n) Θ(lg n) (aol n = t ) nagyságrendjét. 3.3. Feladat. A leftsubtreesrunk(t, d) eljárás mintájára dolgozzuk ki a rigtsubtreesrunk(t, d) eljárást, ennek segédeljárásait, és az eez szükséges kiegyensúlyozási sémát (megoldás:. ábra)! Mutassuk be az AVLdel( t, k) eljárás m ködését néány példán! Mutassunk olyan, legalább négy magasságú fát és kulcsot, amelyre az AVLdel(t, k) eljárás minden, a zikailag törölt csúcs feletti szinten kiegyensúlyozást végez! 3.4. Feladat. Írjuk meg az AVLdel(t, k) eljárás egy olyan változatát, amely abban az esetben, a a k kulcsot olyan bels csúcs tartalmazza, aminek két gyereke van, ezt a törlend csúcsot a bal részfája legynagyobb kulcsú csúcsával elyettesíti! 3.4. Az AVL fák magassága* Tétel: Tetsz leges n csúcsú nemüres AVL fa magasságára: lg n 1, 4 lg n 1
Bizonyítás: Az lg n egyenl tlenség bizonyításáoz elég azt belátni, ogy ez tetsz leges nemüres bináris fára igaz. Egy tetsz leges bináris fa nulladik (gyökér) szintjén legfeljebb = 1 csúcs van, az els szintjén maximum 1 = csúcs, a második szintjén nem több, mint = 4 csúcs. Általában, a az i- edik szinten i csúcs van, akkor az i + 1-edik szinten legfeljebb i+1 csúcs, iszen minden csúcsnak maximum két gyereke van. Innét egy mélység bináris fa csúcsainak n számára n + 1 + +... + = +1 1 < +1. Innét lg n lg n < lg +1 = + 1, amib l lg n. A 1, 4 lg n egyenl tlenség bizonyításáoz elég azt belátni, ogy ez tetsz leges nemüres, kiegyensúlyozott bináris fára (KBF-re) igaz. Eez el ször megatározzuk egy magasságú (azaz nemüres) KBF csúcsainak minimális f számát. Nyilván f = 1 és f 1 =, iszen egy nulla magasságú KBF csak a gyökércsúcsból áll, az egy magasságú KBF-ek pedig ((B)G(J)), ((B)G), vagy (G(J)) alakúak. Az is világos, ogy az < f, f 1, f,... > sorozat szigorúan monoton növekv. (Ennek igazolásáoz vegyünk egy i + 1 magas, f i+1 csúcsú t KBF-et! Ekkor a t bal és jobb részfái közül az egyik magassága i. Legyen ez az f részfa! Jelölje most s(b) egy tetsz leges b bináris fa csúcsainak számát! Akkor f i+1 = s(t) > s(f) f i.) Ezután esetén f = 1 + f 1 + f. (Ha ugyanis t egy magasságú, minimális, azaz f méret KBF, akkor ennek bal és jobb részfáiban is, a részfák magasságáoz mérten a leet legkevesebb csúcs van. Az egyik részfája kötelez en 1 magas, ebben teát f 1 csúcs van. Mivel t KBF, a másik részfája 1 vagy magas. A másik részfában teát f 1 vagy f csúcs van, és f < f 1, teát a másik részfában f csúcs van, és így f = 1 + f 1 + f.) A képlet emlékeztet a Fibonacci sorozatra: F =, F 1 = 1, F = F 1 + F, a. Megjegyzés: A magasságú, és f méret KBF-eket ezért Fibonacci fáknak ívjuk. Az el bbiek szerint egy magasságú Fibonacci fa mindig (ϕ 1 G ϕ ) vagy (ϕ G ϕ 1 ) alakú, aol ϕ 1 és ϕ 1, illetve magasságú Fibonacci fák. 3.. Feladat. Rajzoljunk..4 magasságú Fibonacci fákat! Megvizsgáljuk, van-e valami összefüggés az < f : N > és az < F : N > sorozatok között.
1 3 4 6 7 8 9 F 1 1 3 8 13 1 34 f 1 4 7 1 33 A fenti táblázat alapján az els néány értékre f = F +3 1. Teljes indukcióval könnyen látató, ogy ez tetsz leges egész számra igaz: Feltéve, ogy k 1 esetén igaz, = k + 1-re: f = f k+1 = 1 + f k + f k 1 = 1 + F k+3 1 + F k+ 1 = F k+4 1 = F +3 1. Tudjuk, ogy ( F = 1 1 + ) ( 1 ) Az F -ra vonatkozó explicit képlet segítségével összefüggést adunk tetsz leges KBF n mérete és magassága között. ( n f = F +3 1 = 1 1 + ) +3 ( 1 ) +3 1 ( 1 1 + ) +3 ( 1 ) +3 1 Mivel < < 3, azért 1 ( / < 1 és 1 ) +3 < 1. Eszerint ( n > 1 1 + ) +3 ( 1 1 = 1 1 + ) 3 ( 1 + ) 1 + Mivel ( 1 1 + ) 3 = 1 + 3 + 3( ) + ( ) 3 8 = 16 + 8 8 = + Ezt beelyettesítve az el bbi, n -re vonatkozó egyenl tlenségbe: n > + ( 1 + ) 1 + 3
Az els együttatót tagokra bontva, a disztributív szabállyal: ( 1 + ) ( n > + 1 + ) 1 + Most ( 1 + ) 1 + Eszerint 1 esetén ( 1 + n > ( = -ra pedig n = 1, és így n = ( 1+ ) 1 + ) 1 + ) 1 A fentiekb l tetsz leges, nemüres KBF n méretére és magasságára ( 1 + ) n Innét, a vesszük mindkét oldal kettes alapú logaritmusát, majd lg 1+ -tel osztunk: 1 lg 1+ 1 Mivel 1, 44 < 1, 444 < < 1, 4441 < 1, 4, azért tetsz leges, lg 1+ nemüres KBF n méretére és magasságára lg n 1, 4 lg n 4. Általános fák Az általános fák esetében, a bináris fákkal összeasonlítva, egy csúcsnak tetsz legesen sok gyereke leet. Itt azonban, tetsz leges csúcsoz tartozó részfák száma pontosan egyenl a gyerekek számával, azaz nem tartoznak ozzá üres részfák. Ha a gyerekek sorrendje lényeges, akkor rendezett fákról beszélünk. Általános fákkal modellezetjük például a számítógépünkben a mappák ierarciáját, a programok blokkstruktúráját, a függvénykifejezéseket, a családfákat és bármelyik ierarcikus struktúrát. 4
Vegyük észre, ogy ugyan minden konkrét általános fában van az egy csúcsoz tartozó gyerekek számára valamilyen r fels korlát, de ett l a fa még nem tekintet r-árisnak, mert ez a korlát nem abszolút: a fa tetsz leges csúcsa gyerekeinek száma tetsz legesen növelet. Másrészt, mivel itt nem értelmezzük az üres részfa fogalmát, ez mégsem általánosítása az r-áris fa fogalmának. Értelmezzük azonban a gyökércsúcs (nincs szül je) és a levélcsúcs (nincs gyereke) fogalmát, azaz továbbra is gyökeres fákról beszélünk. Az általános fák természetes ábrázolási módja a bináris láncolt reprezentáció. Itt egy csúcs szerkezete a következ (aol most cild1 az els gyerekre, sibling pedig a következ testvérre mutat). A p csúcs akkor levél, a p cild1 = ; és a p csúcs akkor utolsó testvér, a p sibling =. Node + cild1, sibling : Node* // cild1: els gyerek; sibling: következ testvér + key : T // T ismert típus + Node() { cild1 = sibling = } // egycsúcsú fát képez bel le + Node(x:T) { cild1 = sibling = ; key = x } Természetesen itt is leet szül pointer, ami a gyerekek közös szül jére mutat vissza. 4.1. Feladat. Próbáljunk megadni az általános fákra másfajta láncolt reprezentációkat is! Hasonlítsuk össze az általunk adott ábrázolásokat és a bináris láncolt reprezentációt, memóriaigény és rugalmasság szempontjából! A szöveges (zárójeles) reprezentációban az általános fáknál a gyökeret el re szokás venni. Így egy nemüres fa általános alakja (G t 1... t n ), aol G a gyökércsúcs tartalma, t 1... t n pedig a részfák. Így pl. az { 1 [ () ] (3) [ 4 (6) (7) ] } általános fában az 1 van a gyökérben, a gyerekei a, a 3 és a 4, a ozzájuk tartozó részfák pedig sorban a [ () ], a (3) és a [ 4 (6) (7) ]. A fa levelei az, a 3, a 6 és a 7. 4.. Feladat. Rajzoljuk le a fenti fát! Írjunk programot, ami kiír egy binárisan láncolt fát a fenti zárójeles alakban! Írjunk olyat is, ami egy szövegfájlból visszaolvassa! Készítsük el a kiíró és a visszaolvasó programokat az általunk kidolgozott alternatív láncolt ábrázolásokra is! (A visszaolvasó programokat általában neezebb megírni.) Ellentétben az ún. szabad fákkal, amik irányítatlan, körmentes gráfok.
Az általános fa preorder bejárása 3 a cild1 left és sibling rigt megfeleltetéssel a bináris reprezentáció preorder bejárását igényli. Az általános fa postorder bejárásáoz 4 azonban (az el bbi megfeleltetéssel) a bináris reprezentáció inorder bejárása szükséges. preorder(t) t process(t) preorder(t cild1) t = t sibling postorder(t) t postorder(t cild1) process(t) t = t sibling 4.3. Feladat. Lássuk be a fenti bejárások elyességét! (Ötlet: A testvérek listájának mérete szerinti teljes indukció pl. célravezet.) Lássuk be egy ellenpélda segítségével, ogy az általános fa inorder bejárása 6 nem szimulálató a bináris reprezentáció egyik nevezetes bejárásával 7 sem! Írjuk meg a bináris láncolt reprezentációra az általános fa inorder bejárását és szintfolytonos bejárását is! 4.4. Feladat. Írjuk meg a fenti bejárásokat az általunk adott aletrnatív láncolt reprezentációkra is! Írjunk olyan programokat, amelyek képesek tetsz leges általános fa egy adott reprezentációjából egy adott másik ábrázolású másolatot készíteni!. B+ fák és m veleteik ttp://aszt.inf.elte.u/ asvanyi/ad/b+ fa.pdf 3 el ször a gyökér, majd sorban a gyerekeiez tartozó részfák 4 el bb sorban a gyökér gyerekeiez tartozó részfák, végül a gyökér A fájlrendszerekben általában preorder bejárás szerint keresünk, míg a függvénykifejezéseket (eltekintve most a lusta kiértékelést l, aminek a tárgyalása túl messzire vezetne) postorder bejárással értékeljük ki. 6 A (G t 1... t n ) fára n > esetén el bb t 1 -et járja be, majd feldolgozza G-t, aztán bejárja sorban a t... t n részfákat; n = esetén csak feldolgozza G-t. 7 preorder, inorder, postorder, szintfolytonos 6