2. Adatszerkezetek Az adattípus absztrakciós szintjei http://people.inf.elte.hu/fekete/docs_/adt_ads.pdf Absztrakt adattípus (ADT) Az adattípust úgy specifikáljuk, hogy szerkezetére, reprezentálására, implementálására semmilyen megfontolást, előírást, utalást nem teszünk. A leírásban kizárólag matematikai fogalmak használhatók. Alapvetően két leírási mód terjedt el:. algebrai specifikáció (axiómák megadásával), 2. funkcionális specifikáció (elő- és utófeltétellel). Absztrakt adatszerkezet (ADS) Megmondjuk azt, hogy alapvetően milyen struktúrája van a szóban forgó adattípusnak. Az ADS szintet egy szerkezeti gráf és az ADT szinten bevezetett műveletek alkotják együttesen. A gráf csúcspontjai adatelemeket azonosítanak, az irányított élek pedig a közöttük fennálló rákövetkezési relációt ábrázolják. Az adatszerkezeteket osztályozhatjuk az általuk meghatározott gráf alakja szerint: lineáris, kétirányú vagy szimmetrikus lineáris, fa (struktúrájú), ortogonális többszörösen összefüggő = általános gráf alakú adatszerkezet. Egyszerű adattípusok http://people.inf.elte.hu/fekete/docs_/tomb_verem_sor.pdf Tömbök ADT: Legyen T egy E alaptípus feletti k ( ) dimenziós tömb típus. Vezessük be az I= I... I k indexhalmazt, ahol j [..k]: Ij = [..n j ]. Az A T tömbnek így N = n * n 2 *... * n k eleme van, melyek halmazát jelölje {a,..,a n }. Ekkor mindig van egy f:i->{a,..,a n } kölcsönösen egyértelmű leképzés. Jelölés: A[i,i 2,,i k ] tömbnek az a j elemét jelöli, ha az i,i 2,,i k indexekből alkotott rendezett k- as f szerinti képe a j érték: f(i,i 2,,i k )=j. Műveletek: (melyek bármely k esetén szerepelnek): indexelés (=tömbelem kiválasztása) a már bevezetett A[i,i 2,,i k] kifejezéssel, ami szerepelhet értékadó utasítás bal oldalán, és jobb oldalán is. Így lehetséges:. tömbelem lekérdezése, 2. tömbelem módosítása. A tömb merev adatszerkezet, a mérete nem változik, mert nem lehet a tömbbe egy elemet beszúrni, és nem lehet a tömbből egy elemet kitörölni. k = esetén a vektor, k = 2 esetén a mátrix elnevezés használatos, míg az általános esetben k-dimenziós tömbnek nevezzük az adattípust. ADS Az a megegyezés született, hogy j szerinti rákövetkezést is definiálnak: köv j A[i,,i j,,i k ] = A[i,,i j+,,i k ] (ahol i j < n j ) A vektor egy beosztásokkal ellátott szalag, a 2-dimenziós tömb egy mátrix, a 3-dimenziós tömb egy cellákra osztott téglatest alakját ölti gondolatainkban.
Aritmetikai ábrázolás: Csak akkor használjuk ezt a módot, ha a hatékony ábrázolás úgy kívánja. Az elemeket általában sorfolytonosan, ill. oszlopfolytonosan helyezzük egy vektorba. Láncolt ábrázolás: Tipikusan a hézagosan kitöltött mátrixot (más néven: ritka mátrixot) ábrázolják így. Verem ADT,ADS (a vizsgán se kérdezték ezeket, csak a műveleteket emelem ki, szerintem elég ha átfutjátok egyszer a pdf alapján a többit). Műveletek: Empty: V Üres verem konstans; az üres verem létrehozása IsEmpty: V L A verem üres voltának lekérdezése Push: V E V Elem betétele a verembe Pop: V V E Elem kivétele a veremből Top: V E A felső elem lekérdezése Megszorítások: D Pop = D Top = V \ {Empty} Aritmetikai ábrázolás Bevezetjük a top változót, mely mindig a legfelső elem indexe (top [..max]). A v vermet ábrázoló tömb: v[..max]. A szokásos műveletek mellett használni kell egy új, IsFull műveletet is, mivel a tömb betelhet, és ezt figyelni kell. Láncolt ábrázolás Itt a v változó egy pointert jelöl. Ebben az ábrázolásban v = top, tehát utóbbi felesleges, ezért nem is használjuk. Alkalmazásai: Lengyel forma, helyes zárójelezés feldolgozása stb. (Érdemes ezeket is átfutni egyszer, de nem ezeken van a hangsúly). 2
Sor ADT,ADS Műveletek: Empty: S Üres sor konstans; az üres sor létrehozása IsEmpty: S L A sor üres voltának lekérdezése In: S E S Elem betétele a sorba Out: S S E Elem kivétele a sorból First: S E Az első elem lekérdezése Megszorítások: DOut = DFirst = S \ {Empty} Aritmetikai ábrázolás Az s sor ebben az ábrázolásban egy rekordszerkezet, melynek része egy s[..max] tömb,amely a sor elemeit tartalmazza, egy e {,,max} index, amely mindenkor az első elemre mutat, valamint egy k {,, max} változó, amely a sor elemszámát jelzi. A sor úgy működik, hogy a sor végére rakjuk, ill. az elejéről veszünk ki az elemeket, ezért célszerű a tömb elejére beengedni a tömb végén kilógó elemeket, különben betelne úgy a tömb, hogy egyes indexei felhasználatlanok. Egy tipikus sor lassan körbemegy a tömbön. (A műveletek között szerepel az IsFull is). Láncolt ábrázolás Ekkor az s változó egy pointert jelöl. Az eleje pointert nem használjuk, mivel megegyezik s-sel. A műveletek: A sor alkalmazásai. Egyes pufferelési eljárásokban (pl. klaviatúránál). 2. Gráfok, ill. fák szélességi bejárásánál (pl. bináris fák szintfolytonos bejárása, lásd ott!). 3. Elméleti érdekesség: a sor körbetekerésével szimulálható a verem adatszerkezet, de a sor csak két veremmel valósítható meg. 3
Elsőbbségi sor http://people.inf.elte.hu/fekete/docs_/elsobbsegi_sor.pdf ADT Az egyszerűség kedvéért a P prioritásos sor típusba csak az egyes elemek prioritását tesszük be, az elemet magát nem. Így a sorban csak N-beli elemek vannak.. Műveletek Empty: P Üres IsEmpty: P L Üres? Insert: P N P Sorba Max: P N Első Prior / Első Elem DelMax: P P N Sorból 2. Megszorítások: D Max = D DelMax = P \ {Empty} ADS és reprezentációs szint Műveletigény:. megvalósítás: Rendezetlen tömbbe kerülnek az elemek, pl. a beírás sorrendjében. k jelzi a mindenkori elemszámot. Javítás: Nem nevezhetjük új megvalósítási módnak az alábbiakat, az előzőhez képest csak új változót vezetünk be: maxh mindig a maximális elem indexe (helye). 2. megvalósítás: Rendezett tömbben helyezzük el az elemeket. A maximális elem mindig az utolsó. Kihasználjuk azt, hogy egy rendezett tömbben egy elemet logaritmikus (=bináris) kereséssel meg lehet találni. 4
3. megvalósítás: Egy kupac tulajdonságú fát töltünk fel az elemekkel. A kupac (heap) egy olyan majdnem teljes, balra tömörített bináris fa (tehát az utolsó szint jobb oldaláról esetleg hiányozhatnak elemek), amelyben minden belső elem nagyobb, vagy egyenlő, mint a gyermekei. Insert: Az új csúcs beszúrásával meg kell tartanunk a kupac tulajdonságot, ezért az új csúcs az utolsó sorba, balról az első üres helyre, vagy ha az utolsó sor teljes, akkor egy új sor bal szélére kerül. De még rendbe kell hozni a csúcsok értékeinek viszonyát, tehát elemcseréket végzünk alulról a gyökér felé haladva a megfelelő élsorozaton (tömbnél a megfelelő láncon), ameddig szükséges. Nézzük ennek működését az alábbi kupacon, ha a 5-ös értéket szúrjuk be: Max: Ebben az esetben csak kiolvassuk a gyökérben lévő értéket. DelMax: A gyökérben lévő értéket kiolvassuk, majd a szintfolytonos bejárás során utolsóként bejárt (jobb alsó) értéket rakjuk a gyökérbe, az utolsó csúcs kitörlődik. A kupac tulajdonság megtartása érdekében a gyökérbe került értéket elemcserékkel lesüllyesztjük (mindig a nagyobb gyerekkel cserélve) a megfelelő helyre. Az alábbi kupacon láthatjuk ennek működését: 5
Lista http://people.inf.elte.hu/fekete/docs_/lista.pdf Ezen adatszerkezetnek számos változata van, melyeket az alábbi 3 fajta tulajdonság különbözteti meg egymástól: Egyirányú vagy kétirányú, Egyszerű vagy ciklikus, Fejelemes vagy fejelem nélküli. ADT - Egyirányú, egyszerű lista Műveletek: Üres: L Üres lista konstans; az üres lista létrehozása. Üres?: L L A lista üres voltának lekérdezése. Elsőre: L L Az aktuális elem-komponens első elemre állítása. Következőre: L L Az aktuális elem-komponens következő elemre állítása. Érték: L E Az aktuális elem értékének lekérdezése. Módosít: L E L Az aktuális elem értékének módosítása. Töröl: L L Az aktuális elem törlése. BeszúrUtán: L E L Adott értékű elem beszúrása az aktuális elem után. BeszúrElsőnek: L E L Adott értékű elem beszúrása első elemként. Utolsó?: L L Annak lekérdezése, hogy az aktuális elem az utolsó-e. Láncolt ábrázolás A listát rekordszerkezetként valósítjuk meg, melynek komponensei: A tulajdonképpeni lista pointere (l vagy FEJ), Az aktuális elemre mutató pointer (akt), Hibát jelző logikai változó (hiba), Esetleges további komponensek lehetnek: elemszám, előző, utolsó, stb.. Fejelem nélküli lista: Az akt pointer értéke NIL lesz üres lista esetén, de akkor is, ha lelépünk a listáról. Fejelemes lista: Ennél a megvalósításnál mindig létezik egy fejelem, melyben speciális adat van. Üres lista esetén a fejelem pointere NIL, az akt pointer a fejelemre mutat. 6
Kétirányú, egyszerű lista Fejelem nélküli: Fejelemes: Ciklikus lista (kétirányú) 7
Bináris fák http://people.inf.elte.hu/fekete/docs_/binaris_fak.pdf ADT - Műveletek: Üres: B Üres fa konstans; az üres fa létrehozása. Üres?: B L A fa üres voltának lekérdezése. EgyeleműFa: E B Adott értékkel egyelemű fa létrehozása. BeszúrBalra: B B B Balgyerekkel nem rendelkező fába való beszúrás. BeszúrJobbra: B B B Jobbgyerekkel nem rendelkező fába való beszúrás. Gyökérelem: B E A gyökérelem értékének lekérdezése. MódosítGyökér: B E B A gyökérelem értékének módosítása. Balgyerek: B B B A fa megváltoztatása nélkül a balgyerek-fa lekérdezése. Jobbgyerek: B B B A fa megváltoztatása nélkül a jobbgyerek-fa lekérdezése Töröl: B A fa törlése ADS A fa olyan körmentes irányított gráf, amelyre igazak az alábbiak: Pontosan egy csúcsba nem vezet él (ez a gyökér) A többi csúcsba pontosan egy él vezet Minden csúcs elérhető a gyökérből, mégpedig egyértelműen Az egyes csúcsokból kivezető élek száma minden fára korlátos (r > ) Az elnevezése ekkor: r-áris fa. Gyakorlatban az élek rendezett szelektornevekkel címkézettek. r = 2 esetén beszélünk bináris fákról. Ennél az adatszerkezetnél szokásos jelölések: t: bináris fa Ω: üres fa bal(t) vagy (t): balgyerek-fa jobb(t) vagy (t): jobbgyerek-fa gy(t): gyökérben lévő érték Láncolt reprezentáció Ebben a reprezentációs módban az alábbi jelölések használandók: t = NIL: Ω t^.bal ill. t^.jobb: bal(t) ill. jobb(t) t^.ért: gy(t) Aritmetikai reprezentáció A bináris fa elemeit különböző bejárási módoktól függő sorrendben helyezzük el egy vektorban. Preorder bejárás: ABDECFG Szintfolytonos bejárás: ABCDEFG Inorder bejárás: DBEAFCG Posztorder bejárás: DEBFGCA 8
Gráfok ábrázolása http://people.inf.elte.hu/fekete/docs_2/grafalg/grafalg.pdf Szomszédsági-mátrix (adjacencia-mátrix, csúcsmátrix) Legyen G=(V,E) véges gráf, és n a csúcsok száma. Ekkor a gráfot egy n n -es mátrixban ábrázoljuk, ahol az oszlopokat és a sorokat rendre a csúcsokkal indexeljük (ez leggyakrabban,..,n). Egy mezőben akkor van -es, ha a hozzá tartozó oszlop által meghatározott csúcs szomszédja a sor által meghatározott csúcsnak. azaz A[ i, j] =,, ha ( i, j) E, ha ( i, j) E Szomszédsági lista (éllista) Vegyünk fel, egy mutatókat tartalmazó Adj[..n] tömböt (a csúcsokkal indexeljük a tömböt). A tömbben lévő mutatók mutatnak az éllistákra. Irányított gráf esetén, az éllisták listaelemei reprezentálják az éleket. Az élnek megfelelő listaelemet abban a listában tároljuk, amelyik csúcsból kiindul az él, és a célcsúcs címét (tömb indexét, címkéjét stb.) eltároljuk a listaelemben. Tehát az ( i, j) E él megvalósítása: az i-edik listában egy olyan listaelemmel, amelyben eltároltuk a j-t, mint az él célcsúcsát. Irányítatlan gráf esetén, egy élnek két listaelemet feleltetünk meg, azaz egy irányított élt egy oda-vissza mutató, irányított élpárral valósítunk meg a korábban említett módon. Élsúlyozott gráf esetén, az él súlyát is a listaelemben fogjuk tárolni. Irányított gráf: Helyfoglalás: n + e Irányítatlan gráf: Helyfoglalás: n + 2e 9
Éllistás ábrázolás esetén gyorsan végignézhetjük egy adott csúcsból kiinduló éleket. Szomszédsági-mátrix ábrázolás esetén gyorsan eldönthető, hogy egy (i,j) pár éle-e a gráfnak