2012. március 27. A PROGAMOZÁS ALAPJAI 1 Vitéz András egyetemi adjunktus BME Híradástechnikai Tanszék vitez@hit.bme.hu Miről lesz ma szó? Dinamikus adatszerkezetek Önhivatkozó struktúra keresés, beszúrás, törlés strázsák használata Prioritásos sor Két irányban láncolt lista Több kulcs szerint rendezett lista Fésűs lista Memória gazdálkodás Dinamikus adatszerkezetek Dinamikus adatszerkezetek Dinamikus adatszerkezetek: Adott építőelemekből, adott szabályok szerint felépített, de nem rögzített méretű adatszerkezetek. A tényleges méret futás közben alakul ki. A méret és a szerkezet is megváltoztatható. Megvalósításához szükséges: önhivatkozó struktúra olyan összetett adattípus, amely összetevőként tartalmaz olyan mutatót, amely saját típusára képes mutatni. Önhivatkozó struktúrák Önhivatkozó struktúrák Struktúra deklaráció alakja: struct [<struktúra címke>] { <struktúra tag deklarációk> [<struktúra változó azonosítók>] ; Önhivatkozó struktúra deklaráció alakja: struct <struktúra címke> { [<struktúra tag deklarációk>] struct <struktúra címke> * <azonosítók>; [<struktúra tag deklarációk>] [<struktúra változó azonosítók>] ; 1
Önhivatkozó struktúrák Önhivatkozó struktúrák Példa: Példa: struct elem1 { int adat; struct elem1 * kovetkezo; e1,*e2; struct elem1 e3,*e4; typedef struct kamu { int adat; struct kamu * kovetkezo; elem2; elem2 e5,*e6; Önhivatkozó struktúrák Példa: : typedef struct k { int adat; struct k * kovetkezo; listelem,*listptr; listelem e7,e8; listptr p1,p2; : Hogy kezelni tudjuk, két mutatóra van szükségünk: Az egyik az adatszerkezet elejét jelzi, A másik az éppen feldolgozott elemre mutat. A listát ez a két mutató testesíti meg. 2
A beszúráshoz egy segédmutató kell. Hogy a tárolt adatokat vissza tudjuk keresni, rendezni kell (vagy eleve rendezetten felépíteni) Kézenfekvő megoldás: közvetlen beszúrásos rendezés. Beszúrni csak az aktuális elem mögé lehet! Beszúrni csak az aktuális elem mögé lehet! ->=->; Beszúrni csak az aktuális elem mögé lehet! ->=->; Beszúrni csak az aktuális elem mögé lehet! ->=; 3
Beszúrni csak az aktuális elem mögé lehet! Beszúrni csak az aktuális elem mögé lehet! ->=; A beszúrás helyét lineáris kereséssel találhatjuk meg: A legelső elemtől kiindulva megkeressük az első olyan elemet, amely után következő elem kulcsa nagyobb a beszúrni kívánténál. Ez a módszer hibás! Nem lehet beszúrni az első elem elé, és az utolsó elem után. Ezek esetleges szükségességét minden esetben külön meg kell vizsgálni. Ez körülményes, ezért nem így járunk el. Helyette: strázsával: Mindkét végén kiegészítjük a listát egy-egy elemmel. Egy olyannal, amelynek kulcsa kisebb minden elvileg tárolható eleménél, és egy olyannal, amelynek kulcsa nagyobb minden elvileg tárolható eleménél. Ezeket az elemeket strázsának (sentinel) nevezzük. 4
Az üres lista ezek szerint: Elemet törölni is csak az aktuális elem mögül lehet! Elemet törölni is csak az aktuális elem mögül lehet! ->=->->; Elemet törölni is csak az aktuális elem mögül lehet! ->=->->; Elemet törölni is csak az aktuális elem mögül lehet! Elemet törölni is csak az aktuális elem mögül lehet! 5
Adat beszúrása a listába typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; insert(data newdata,lp ){ lp =,=malloc(sizeof (le)); if(!)return 0; ->d=newdata; while(->->d.key<newdata.key)=->; ->=->; ->=; return 1; Adat beszúrása a listába cserével typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; insert_x(data newdata,lp ){ lp =,=malloc(sizeof (le)); if(!)return 0; while(->d.key<newdata.key)=->; ->=->; ->=; ->d=->d; ->d=newdata; return 1; Csak akkor, ha külső hivatkozások nem mutatnak a listára! Adott kulcsú adat törlése a listából typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; delete(key_t k,lp ){ lp,=; while((k!=->->d.key)&& (k<->->d.key))=->; if(k>->->d.key)return 0; =->; ->=->->; free(); return 1; Adott kulcsú adat törlése a listából cserével typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; delete_x(key_t k,lp ){ lp =; while((k!=->d.key)&&(k<->d.key)) =->; if(k>->d.key)return 0; ->d=->->d; =->; ->=->->; free(); return 1; Csak akkor, ha külső hivatkozások nem mutatnak a listára! Listák különleges alkalmazásai: verem (stack) (LIFO), várakozási sor (queue) (FIFO) prioritásos sor (priority queue) (Last In First Out): Elemet mindig csak a lista elejére szúrunk be; mindig csak a lista elejéről veszünk ki. 6
Beszúrás a verembe: A vermet egyetlen mutató testesíti meg. Beszúrni üres verembe is ugyanúgy lehet. Törölni az utolsó elemet is ugyanúgy lehet. Strázsákra sincs szükség. Beszúrás a verembe: Beszúrás a verembe: ->=; ->=; Beszúrás a verembe: Beszúrás a verembe: =; =; 7
Beszúrás a verembe: Beszúrás üres verembe: Beszúrás üres verembe: Beszúrás üres verembe: ->=; ->=; Beszúrás üres verembe: Beszúrás üres verembe: =; =; 8
Beszúrás üres verembe: Elem törlése a veremből: Elem törlése a veremből: Elem törlése a veremből: =->; =->; Elem törlése a veremből: Elem törlése a veremből: 9
Az utolsó elem törlése a veremből: Az utolsó elem törlése a veremből: =->; Az utolsó elem törlése a veremből: Az utolsó elem törlése a veremből: =->; Adat berakása a verembe typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; lp push(data newdata,lp*stack){ lp =malloc(sizeof (le)); if(!)return 0; ->d=newdata; ->=*stack; *stack=; return*stack; A megváltozott címet adjuk vissza! VIGYÁZAT! lp*stack kétszeres indirekció! Adat elővétele a veremből typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; data pop(lp*stack){ data dd;lp ; =*stack; dd=->d; *stack=->; free(); return dd; A kiolvasott adatot adjuk vissza! VIGYÁZAT! lp*stack kétszeres indirekció! 10
(First In First Out): Elemet mindig csak a lista végére szúrunk be; mindig csak a lista elejéről veszünk ki. A várakozási sort két mutató testesíti meg. Az egyik az első elemre mutat; a másik pedig az utolsóra. Beszúrni az utolsó elem mögé lehet. Egy strázsa elég az elejére, így mindig lesz utolsó elem, akkor is, ha a sor üres. Törölni mindig a strázsa utáni elemet kell. : Üres várakozási sor: Beszúrás várakozási sorba: Beszúrás várakozási sorba: ->=0; 11
Beszúrás várakozási sorba: Beszúrás várakozási sorba: ->=0; ->=; Beszúrás várakozási sorba: Beszúrás várakozási sorba: ->=; =; Beszúrás várakozási sorba: Beszúrás várakozási sorba: =; 12
Beszúrás üres várakozási sorba: Beszúrás üres várakozási sorba: ->=0; Beszúrás üres várakozási sorba: ->=0; Beszúrás üres várakozási sorba: ->=; Beszúrás üres várakozási sorba: Beszúrás üres várakozási sorba: ->=; =; 13
Beszúrás üres várakozási sorba: Beszúrás üres várakozási sorba: =; Elem törlése várakozási sorból: Elem törlése várakozási sorból: ->=->->; Elem törlése várakozási sorból: Elem törlése várakozási sorból: ->=->->; 14
Elem törlése várakozási sorból: Utolsó elem törlése várakozási sorból: Utolsó elem törlése várakozási sorból: ->=->->; Utolsó elem törlése várakozási sorból: ->=->->; Utolsó elem törlése várakozási sorból: Utolsó elem törlése várakozási sorból: if(!(->))=;!! 15
Utolsó elem törlése várakozási sorból: Utolsó elem törlése várakozási sorból: if(!(->))=; Prioritásos sor Prioritásos sor Prioritásos sor: Prioritásos sor: Elemet mindig csak a magasabb prioritású vagy megegyező prioritású de régebben várakozó elemek mögé szúrunk be; Prioritásos sor Prioritásos sor Prioritásos sor: Elemet mindig csak a magasabb prioritású vagy megegyező prioritású de régebben várakozó elemek mögé szúrunk be; mindig csak a lista elejéről veszünk ki. Megvalósítható a lista első változatával. Mivel beszúrásra a legelején és a legvégén is sor kerülhet, kell mindkét strázsa. 16
Prioritásos sor Beszúrás prioritásos sorba: Prioritásos sor Beszúrás prioritásos sorba: Prioritásos sor Beszúrás prioritásos sorba: Prioritásos sor Beszúrás prioritásos sorba: Két irányban láncolt lista Ez NEM fa, de nagyon gyakran használjuk! Két irányban láncolt lista Ez NEM fa, de nagyon gyakran használjuk! Két irányban láncolt lista strázsával: prev prev prev prev prev prev prev prev prev prev 17
Két irányban láncolt lista Ez NEM fa, de nagyon gyakran használjuk! Két irányban láncolt lista strázsával: Két irányban láncolt lista Ez NEM fa, de nagyon gyakran használjuk! Két irányban láncolt lista strázsával: prev prev prev prev prev Rekurzívan rendezhető gyorsrendezéssel! prev prev prev prev prev Cserénél vigyázni kell a mutatók átállításának sorrendjére! Több kulcs szerint rendezett lista A lista szerkezet lehetővé teszi, hogy egy lista egyidejűleg több szempont szerint is rendezett legyen. Például az iskolaorvos a gyerekeket testsúly szerint csökkenő, testmagasság szerint növekvő sorban tárolhatja. Ilyenkor minden kulcshoz tartozik egy-egy mutató. Ezeket célszerűen adatvektorba foglalhatjuk. A vektor indexelése történhet felsorolt típussal. Több kulcs szerint rendezett lista Listaelem több kulcs szerinti láncoláshoz: #define N struct multisort { [<struktúra tag deklarációk>] struct multisort * [N]; [<struktúra tag deklarációk>] [<struktúra változó azonosítók>] ; Fésűs lista Fésűs lista A listaelem tagjai között gyakran tárolunk olyan mutatót, amelyen egy másik lista lóg. A fésű lelógó fogai általában egy másik adattípushoz tartoznak. Példa: A földrajztanár az osztály névsorához hozzárendeli a kapott osztályzatokat. 9.c AAAA Balogh Buda Kiss Döme Rák Emil ZZZZ felelet 3 röpdolgozat 2 témazáró 5 témazáró 4 felelet 4 felelet 5 felelet 5 röpdolgozat 5 röpdolgozat 4 18
Fésűs lista typedef enum{ felelet, ropdolgozat, temazaro jegy_t; typedef struct x{ jegy_tipus miert; short osztalyzat; struct x *kovetkezo_jegy; jegy,*jegy_mutato; typedef struct y{ char nev[48]; jegy_mutato jegyek; struct y *kovetkezo tanulo; tanulo,*tanulo_mutato; typedef struct { char osztaly_neve[4]; tanulo_mutato tanulok; osztaly; A lista elemeit nem célszerű egyenként kérni, mert a nyilvántartáshoz szükséges információ minden egyes elemnél tárolási igényként jelentkezik. Egyszerre kérünk egy nagyobb csomagot, és a kapott tömböt verem szervezésű listává láncoljuk. Az új elem iránti igényeket ebből szolgáljuk ki. Ha elfogy, kérünk még. Ha menet közben visszaadnánk egy elemet, akkor ebbe a tartalék listába tesszük. Ha egy adatelemnek több mutatója is van, csak az egyiket használjuk a láncoláshoz. Memóriagazdálkodás Memóriagazdálkodás #define N 64 typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; lp spare; /*!!! globális változó!!! */ lp getnew(){ lp ; if(!spare){ int i; spare=calloc(n,sizeof(le)); if(!spare) return 0; for(i=0;i<n-1;i++) spare[i].=spare+i+1; =spare; spare=spare->; return ; Memóriagazdálkodás #define N 64 typedef struct{... key_t key;...data; typedef struct x{data d; struct x *;le,*lp; lp spare; /*!!! globális változó!!! */ void hang_up(lp ){ ->=spare; spare=; 19