Láncolt Listák Adatszerkezetek Adatszerkezet: Az adatelemek egy olyan véges halmaza, amelyben az adatelemek között szerkezeti összefüggések vannak Megvalósítások: - Tömb, Láncolt lista, Fa, Kupac, Gráf, Hasító táblázat Egyéb szerkezetek: - Sor, Verem Lista Alapfogalmak Lista (list): 0 vagy több elem véges sorozata - Ha az emelek mindegyikének azonos a típusa: T, akkor azt mondjuk, hogy a lista típusa T-k listája - A lista (a 1, a 2, a n ) ahol a i a lista elemei - Lista hossza: az elemek előfordulási száma a listában (az ismétlődéseket belevesszük) - Üres lista (empty): ha a lista hossza 0, jele: ε - Részlista (sublist): L = (a 1, a 2, a n ) lista, akkor minden i-re, j-re 1<= i <= j <= n (a i, a i+1, a j ) - L és ε minden listának részlistája - Részsorozat (subsequence): Egy L = (a 1, a 2, a n ) listából kapjuk, ha 0, vagy több elemet elhagyunk a listából. A megmaradt elemeknek ugyanolyan sorrendben kell a részsorozatban szerepelniük. L és ε minden listának részsorozata. - Előtag, utótag (prefix, suffix): az előtag olyan részlista, ami az első elemmel, az utótag az utolsó elemmel végződik. ε minden listának előtagja és utótagja. - Elem pozíciója: hányadik helyen szerepel a listában, használjuk az előző és következő jelzőket Láncolt Lista definíció - Láncolt Lista: ~ olyan adatszerkezet, amelynek minden eleme tartalmaz egy hivatkozást egy másik, ugyanolyan típusú elemre. - Tárgyalt altípusok: egyszeresen láncolt, rendezett láncolt, láncolt lista strázsa elemekkel, speciálisan láncolt lista (kétirányú, többszörösen láncolt, ciklikus) Láncolt Lista eleme - Láncolt lista egy elemének definíciója: TLáncElem = Struktúra(tartalom, következő) - Tartalom (tart): a tárolandó adat (egyszerű, összetett típus, objektum-referencia) - Következő (köv): hivatkozás a láncolt lista következő elemére (tömb index, mutató, objektumreferenia) - A lista műveleteit megvalósító algoritmusok a konkrét implementációtól függetlenek, ezért ezzel a későbbiekben nem foglalkozunk. Egyirányú Láncolt Lista jellemzői Adat1 Adat2 Adat3 ø Adat1 Adat2 ø Adat3 - a lánc minden eleme tartalmaz egy hivatkozást a következő címre - a lánc első elemének címét a lista tartalmazza ( változó) - a lista nem tartalmaz tartalmi részt - a lánc végét az utolsó elem következő részének (köv) egy kitűntetett értéke jelzi (0) (ennek valódi értéke az implementációtól függ) - üres listánál a lista értéke ez a kitűntetett érték (0)
Miért használjuk - A tömb adatszerkezet hátrányai, amelyek miatt érdemes listát használni o Méret nem dinamikus (dinamikus tömb?) o Összefüggő memóriaterületet igényel o Beszúrás nehézkes o Törlés nehézkes Miért ne használjuk - a lista adatszerkezet hátrányai, amelyek miatt érdemes lehet tömböt használni o Bonyolult (nehézkes, hibalehetőség) o Elemek nem érthetőek el közvetlenül indexeléssel o Keresés nehezen gyorsítható, alapesetben csak lineáris keresés használható o A következő elemre való hivatkozás eltárolása miatt nagyobb egy elem helyfoglalása Láncolt Lista bejárása - Bejárás: az adatszerkezet valamennyi elemének egyszeri elérése (feldolgozása) - Ez a feldolgozás lehet tetszőleges művelet a lista egy elemének tartalmával. A teljes listára vonatkozó műveleteket így az egyes elemekre külön-külön elvégezhető műveletek sorozatára kell bontani (pl átlagszámítás: összegzés, megszámlálás) Bejárás algoritmusa Eljárás Bejárás() p! Ciklus amíg p ø Feldolgozás (p.tart) p! p. köv Ciklus vége Eljárás vége Keresés listában - Keresés: A lista ismeretében egy megadott tartalmú elem megkeresése o Eldönteni, hogy van-e ilyen tartalmú elem o Ha van, akkor melyik elem az Függvény Keresés(, mitkeres) p! Ciklus amíg (p ø ) p! p.köv Ciklus vége van! (p ø ) Ha van akkor Keresés! p Függvény Vége Új elem felvétele - Lista inicializálása Eljárás Inicializálás()! ø - Lista elejére új elem beszúrása Eljárás ElejéreBeszúrás(, újérték) ElemLétrehozás(uj) uj.tart! újérték uj.köv!! uj - Lista végére beszúrása Eljárás VégéreBeszúrás(, újérték) ElemLétrehozás(új) uj.tart! újérték uj.köv! ø Ha = ø! uj p! Ciklus amíg p.köv ø p! p.köv p.köv! új
Megadott helyre beszúrás Eljárás MegadottHelyreBeszúrás(, n újérték) ElemLétrehozás(új) uj.tart! újérték Ha ( = ø) vagy (n=1) akkor uj.köv = ; =uj; p! ; i! 2 Ciklus amíg (p.köv ø) és (i<n) p! p.köv; i! i+1 új.köv! p.köv p.köv! uj Megadott tartalmú elem törlése Eljárás Törlés(, törlendő) p! ; e! ø Ciklus amíg(p ø) és (p.tart törlendő) e!p p!p.köv Ha p ø akkor Ha e= ø akkor =p.köv e.köv=p.köv Elemfelszabadítás(p) nincs ilyen elem Összes elem törlése Eljárás ListaTörlés() Ciklus amíg ( ø) p!!.köv Elemfelszabadítás(p) Rendezett Láncolt Lista - Rendezett Láncolt Lista: A láncolt lista elemei tartalmuk szerint valamilyen előre definiált rendezési szempont szerint sorrendben helyezkednek el A E X ø - Utólagos rendezés nehézkes, emiatt a lista felépítésekor célszerű rendezni (beszúró rendezés erre alkalmas) - Az előzőek kibővítése az alábbiakkal: o KeresésRendezettben o RendezettBeszúrás Keresés rendezett Listában Függvény KeresésRendezettben(, mitkeres) p! Ciklus amíg (p ø) és (p.tart < mitkeres) p!p.köv van! (p ø) és (p.tart = mitkeres) Ha van akkor Keresés! p Függvény Vége Rendezett Láncolt Lista beszúrás ElemLétrehozás(uj); uj.tart!újérték Ha = ø # ha üres a lista új.köv! ø ;!uj; különben Ha.tart > újérték # első elem elé uj.köv!;!uj; p!; e! ø Ciklus amíg (p ø) és (p.tart < újérték) e!p; p!p.köv; Ha p = ø # utolsó elem mögé uj.köv = ø ; e.köv = uj; különben # két elem közé uj.köv = p; e.köv = uj;
Rendezett Láncolt Lista beszúrás (Azonos ágak összevonása után) Eljárás RendezettBeszúrás(, érték) Elemlétrehozás(új) uj.tart!újérték p!; e! ø Ciklus amíg (p ø) és (p.tart < újérték) e! p; p!p.köv Ha e = ø akkor uj.köv!;!uj; különben uj.köv! p e.köv! uj Miért rendezzünk? Keresési lehetőségek és átlagos lépésszám: Adatszerk.\Algoritmus Lineáris Keresés Logaritmikus keresés Tömb Ordo(n) X Rendezett Tömb Ordo(n) Ordo(log 2 n) Láncolt Lista Ordo(n) X Rendezett Láncolt Lista Ordo(n) x * Ezzel a keresés nem gyorsítható Az utóbbi kettő egyszerű (és igen hatékony) algoritmus a kivételes esetek miatt nem használható, mivel (Legyen!) - Beszúrás: üres lista elején nincs elem - Törlés: utolsó elem után nincs elem Néhány optimalizálási ötlet Mutatott Elem mögé beszúrás Eljárás MutatottMögéBeszúrás(p, újérték) Elemlétrehozás(új) uj.tart!újérték uj.köv!p.köv p.köv!uj Mutatott Elem elé beszúrás Eljárás MutatottEléBeszúrás(p, újérték) ElemLétrehozás(új) uj.tart!p.tart uj.köv!p.köv p.köv!uj p.tart!újérték Mutatott Elem törlése ( nem ismert) Eljárás MutatottMögéBeszúrás(p) q!p.köv p.tart!q.tart p.köv!q.köv Elemfelszabadítás(p) Eljárás vég
Strázsa technika - Strázsa(sentinel) elemek: A lista elejére és a végére felvett kiegészítő elemek. Értékes adatot nem tárolnak, csak technikai szerepük van, hogy kiküszöböljék a kivételes eseteket. - Elérhető előnyök: egyszerűbb beszúrás/törlés, gyorsabb beszúrás/törlés - Szükséges kompromisszumok: helyfoglalás, bejárás körülményesebb Példa a strázsa elemekre: -α 2 + α ø Technikai megvalósítása Strázsa elem megkülönböztetése - Kiegészítő tulajdonság alapján - Tartalom alapján Rendezett Lista esetén az első strázsa célszerűen az adott implementációban a legkisebb, az utolsó pedig a lehető legnagyobb értéket tartalmazza. Lista inicializálása ( az üres lista is tartalmaz elemeket) Eljárás StrázsaInicializálás() Elem létrehozása(st2) st2.tart! + st2.köv! ø ElemLétrehozás(st1) st1.tart! - st1.köv!st2!st1 Kétirányú láncolt lista -α +α ø ø 1 2 ø 3 ø vége - A lánc minden eleme tartalmaz egy hivatkozást a sorban rákövetkező, illetve a sorban őt megelőző elemre is - A lánc végét az utolsó elem a következő részének egy kitűntetett értéke jelzi (példában ø) - A lánc elejét az első elem előző részének egy kitűntetett értéke jelzi (példában ø) - A lánc első elemének ét a lista tartalmazza (példában változó) - A lánc utolsó elemének címét is célszerű tárolni egy külső változóban (vége) Előnyei az egyirányú listához képest - keresés: Ha van információnk az elem listabeli elhelyezkedéséről (tudjuk, hogy vége felé helyezkedik el) előnyös lehet - törlés: előnyös, hiszen azonnal elérhetjük a szomszédos elemet - beszúrás: szintén kihasználható a beláncolás során, hogy közvetlenül elérhető minden elem őt megelőző eleme Hátrányok az egyirányú listához képest - nagyobb elemenkénti helyfoglalás (az előző elem címét tartalmazó mező miatt) - módosításkor bonyolultabb algoritmusra van szükség, mivel az előző hivatkozást is mindig aktualizálni kell
Többszörösen láncolt listák 1 2 1 ø 2 ø 3 ø 3 - A lánc minden eleme tartalmazza n darab következő elem címét - A lánc végét az utolsó elem megfelelő következő részének egy kitűntetett értéke jelzi(ø) - A lánc tartalmaz n darab listaet - Műveletei gyakorlatilag megfelelnek az egyszerű láncolt listánál megismertekkel, felfogható n darab független láncolt listaként - A tartalmi rész azonban egyszer szerepel, emiatt: o Kisebb helyfoglalás o Módosításkor egy időben minden láncban módosul a tartalmi rész (ez nyilvánvalóan csak akkor előny, ha ez a cél) Ciklikusan láncolt lista 1 2 3 - A lánc minden eleme tartalmazza a következő elem címét, az utolsó elem pedig az elsőre mutat vissza - A lánc végét külön nem jelöljük, a bejáró algoritmus felelőssége, hogy észrevegye, ha már feldolgozott minden elemet - A láncba akár több belépési pont is tárolható - A lista lehet egy illetve kétirányú is Előnyei az egyszerű láncolt listához képest: - speciális feladatoknál hasznos (fix méretű adatszerkezet) - törlés: nincsenek kivételes első és utolsó elemek - beszúrás: nincsenek kivételes első és utolsó elemek Statikus megvalósítás - Statikus láncolt lista: A láncolt lista elemeit egy tömbben tároljuk, a listaelem következő mezője a listában következő elem indexét tartalmazza. - A egész típusú változó, az első elem indexé tárolja - A tartalom és a következő mező eltárolható két különböző tömbben is Miklós 4 Zsuzsa -1 András 1 Palika 5 Tilda 6 Tóni 2 Fej = 3 Implementációs részletek: - A lezáró elem lehet egy tetszőleges, indexként nem értelmezhető szám, pl: -1 - Értelemszerűen üres lista esetén = -1 - ElemFelszabadítás(p: Egész) - A listát tartalmazó tömb p. elemét bejelöli töröltként. Az állapot tárolható az elem egy mezőjében vagy egy másik tömbben - ElemLétrehozás(új: Egész) - A listát tartalmazó tömbben keres egy még szabad helyet - Dinamikus tömb esetén ha nincs hely, növeli a tömb méretét - Lefoglalja ezt a helyet - Az új változóban visszaadja az indexet
Dinamikus megvalósítás - Dinamikus láncolt lista: A láncolt lista elemeit dinamikus memóriakezelés segítségével hozzuk létre, a következő mező értéke így egy mutató lesz a lista következő elemének memóriabeli címére. - Minden elem következő mezője egy mutató, ami a lista következő elemének címét tárolja Miklós András Palika Zsuzsa 0 Tilda Tóni Implementációs részlet - Fej egy mutató típusú változó, ami az első elem memóriabeli címét tárolja - A lezáró elem lehet egy 0 mutató (null, nil, stb) - Értelemszerűen ez jelzi a lista végét, ill. az üres listát is - ElemFelszabadítás (p: Mutató) o Felszabadítja a p mutató által mutatott helyen található TListaElem méretű memóriaterületet - ElemLétrehozás (új: Mutató) o Dinamikus memóriakezelés segítségével lefoglal helyet egy TListaElem tárolására o Az új változóban visszaadja a lefoglalt memóriaterület címét Láncolt Lista Objektum-orientált megvalósítása - Technikai megvalósítás tekintetében tulajdonképpen megegyezik a dinamikus megoldással o TListaElemStruktúra TListaElem Osztály o Mutató Objektum referenci o Memória foglalás Konstruktor hívás o Memória felszabadítás Destruktor hívás