Adatszerkezetek 2. Dr. Iványi Péter 1
Hash tábla A bináris fáknál O(log n) a legjobb eset a keresésre. Ha valamilyen közvetlen címzést használunk, akkor akár O(1) is elérhető. A hash tábla a tömb általánosításaként is felfogható, ahol az index bármilyen érték lehet. Ugyanakkor több lehetséges kulcs van mint hely a táblában 2
Hash tábla Tömbnél az index: egész szám Hash tábla: valós szám, szöveg, egy másik tömb vagy bármi egyéb... Az index: a kulcs A hash táblába tárolt adat: az érték 3
Primitív hash tábla, tömb n elemű tömbben akarjuk tárolni a megadott adatokat Feltételezzük, hogy a kulcs csak egész szám Maradék osztást használunk insert(index, ertek) { tomb[index mod n] = ertek; } 4
Hash tábla Trükk: a kulcsból tömb indexet hozunk létre Például, ha adva van egy név, akkor szeretnénk megtudni a hozzátartozó telefonszámot 5
Hash függvény A hash táblák alapja a megfelelően megválasztott hash függvény. Irodalom tartalmaz jót és rosszat is Nincs univerzális megegyezés arról mi tekinthető jónak Rosszul választott függvény kulcs ütközéshez (collision) vezet Kulcsok csoportosulása (clustering): Ebben az esetben annak a valószínűsége hogy több kulcs ugyanazt az indexet adja meg jóval nagyobb mintha egy véletlen szám generátor függvényt használtunk volna. 6
Függvény minősége: Hash függvény Egyszerűség (forrás kódban a sorok száma) Sebesség (időmérés, benchmark) Erő mérése: Statisztikai tesztek melyek megállapítják, hogy a hash függvény mennyiben különbözik a véletlen szám generáló függvénytől Talán a legfontosabb mérési módszer a hash függvény jóságának a mérésére, hogy a függvényre jellemző-e az Avalanche effektus 7
Avalanche effektus Ha a bemenetet kicsit megváltoztatjuk (például csak egy bitet átállítunk) akkor a kimenet jelentősen megváltozik (például az eredménynek több mint a fele megváltozik) 8
Hash függvény A hash függvény akkor jó, ha: amikor a bemenet egy bitje megváltozik, akkor a kimenet minden bitje legalább 50 %-os valószínűséggel változik meg 9
Ütközések feloldása Ha két kulcs ugyanazt az indexet adja, akkor nem lehet két rekordot ugyanaz a helyen tárolni. Ebben az esetben egy másik helyet (indexet) kell találni. Feloldás túlcsordulás kezeléssel Láncolás (chaining) Alternatív címzés (open addressing) 10
Túlcsordulás kezelés tabla[n]; overflow[max_overflow]; end_index = 0; Insert(kulcs, ertek) { h = hash(kulcs) mod n; if(tabla[h]!= ures) { if(end_index == MAX_OVERFLOW) tabla betelt; overflow[end] = kulcs és ertek; end = end + 1; } else tabla[h] = kulcs és ertek; } 11
Többféle probléma Túlcsordulás kezelés A túlcsordulás tömböt is végig kell keresni, Ha nagy a túlcsordulás tömb lassítja a keresést, törlést, beillesztést, O(n) lesz ismét 12
Láncolás A hash táblában minden rekord valójában egy láncolt lista Keresés, törlés kiegészül azzal, hogy a listán végig kell menni 13
Láncolás Elvileg szükség lehet rendezett listára, hogy gyorsabban kereshessünk, DE Ha jó a hash függvény akkor a listák rövidek és nincs szükség a rendezésre, mert a keresés a rendezetlen listában így is nagyon rövid lesz! 14
Alternatív címzés Alternatív címet keresünk ütközés esetén Lineáris próba Quadratikus próba Dupla hashing 15
Példa: Lineáris próba A hash tábla legyen egy 11 elemű tömb insert(kulcs, ertek) 0 1 2 3 4 5 6 7 8 10 9 { h = hash(kulcs) mod n; while(tabla[h]!= ures) { h = (h + 1) mod n; } tabla[h] = kulcs és ertek; } 16
Lineáris próba 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 426 426 mod 11 = 8 426 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 921 921 mod 11 = 8 426 921 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 715 715 mod 11 = 0 426 715 921 17
Lineáris próba 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 129 129 mod 11 = 8 426 715 921 129 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 514 514 mod 11 = 8 426 715 514 921 129 0 1 2 3 4 5 6 7 8 10 9 Beillesztés: 287 287 mod 11 = 1 426 715 514 287 921 129 18
Lineáris próba Jellemző rá a csoportosulás (clustering) Előző példában a 8-az index esetén egyre nehezebb adatot beilleszteni. Csökkenti az algoritmus sebességét Lehetőleg csak félig legyen tele a hash tábla 19
Lineáris próba A lépésköz általában 1, bár lehet mást is használni Működés feltételei: A hash tábla mérete vagy prím szám legyen vagy kettő hatványa Ha a tábla mérete kettő hatványa akkor a lépésköz csak páratlan lehet 20
Lineáris próba, törlés A törlés problematikusabb Ha valóban törlünk hibás működést kapnánk Ha csak törlünk egy elemet, akkor a következő beillesztésnél a lináris próba idő elött leállhat 21
Lineáris próba, törlés A törlés problematikusabb Ha valóban törlünk hibás működést kapnánk Példa megoldás, de a beillesztést is módosítani kell remove(kulcs) { } h = hash(kulcs) mod n; while(tabla[h]!= ures) { } h = (h + 1) mod n; tabla[h] = DELETED; insert(kulcs, ertek) { } h = hash(kulcs) mod n; while( { } tabla[h]!= ures && tabla[h]!= DELETED) h = (h + 1) mod n; tabla[h] = kulcs és ertek; 22
Quadratikus próba Nem konstans lépést használunk. insert(kulcs, ertek) { h = hash(kulcs) mod n; for(step = 1; tabla[h]!= ures; step++) { h = (h + step * step) mod n; } tabla[h] = kulcs és ertek; } 23
Quadratikus próba Nem jön létre csoportosulás Csak akkor garantált a működés ha: A tábla maximum félig telik meg (félig üres) A tábla mérete prím szám 24
Dupla hashing Két hash függvényt használunk A lépésközt is hash függvény határozza meg insert(kulcs, ertek) { h = hash(kulcs) mod n; h2 = (hash(kulcs) mod (n 1)) + 1 while(tabla[h]!= ures) { h = (h + h2) mod n; } tabla[h] = kulcs és ertek; } 25
Működés feltételei: Dupla hashing h2 soha nem lehet nulla, mert végtelen ciklust kapnánk A hash tábla mérete vagy prím szám legyen vagy kettő hatványa Ha a tábla mérete kettő hatványa akkor a lépésköz csak páratlan lehet 26
Rehashing Néha a tábla megtelik, vagy a műveletek nagyon lelassulnak, ilyenkor új táblát kell felépíteni A régi tábla minden élő kulcsát kivesszük és az új nagyobb táblába áttesszük Lassú és számítástechnikailag drága, ritkán alkalmazzuk Másik módszer hozzunk létre egy új táblát és ellenőrizzük mindkét táblában ha keresünk amikor az új táblába illesztünk, akkor a régiből vigyünk át k db elemet Ha minden elemet átvittünk, töröljük a régi táblát 27
Legjobb eset: O(1) Legrosszabb eset: O(n) Teljesítmény Legnagyobb hatással a teher faktor (load factor) van: használt helyek / tábla mérete Mindegyik ütközés feloldás működik 0.5 alatt. 0.5 felett folyamatosan romlik a teljesítmény 28
Objektumok Az objektumok olyan zárt programozási egységek, amelyek a kezelni kívánt adatokon kívűl tartalmazzák azokat az eljárásokat és függvényeket is amelyek az objektumok megfelelő kezelésére képesek. 3 tulajdonság Egységbe zárás Öröklődés Többrétűség (polimorfizmus) 29
Encapsulation Egységbe zárás Az adatmezőkön kívül a kezelő eljárások és függvények is részei az adatstruktúrának Az objektum adatait manipulálni képes függvényeket és eljárásokat metódusnak nevezzük, 30
Konstruktor: Speciális metódusok Akkor fut le amikor az objektum futás közben létrejön Biztosítja, hogy az adatokhoz szükséges memória rendelkezésre álljon Destruktor: Akkor fut le, amikor az objektum megszűnik Memória területek felszabadítása 31
Öröklődés Minsig van egy ős objektum Objektumokból származtatunk újabb objektumokat A leszármaztatott objektumok öröklik őseik tulajdonságait, azaz minden eljárást, metódust és adatmezőt, amivel azok rendelkeznek. 32
Többrétűség (polimorfizmus) Ha egy leszármaztatott objektum metódusát ugyanolyan néven definiálunk, mint egy ősének a metódusát Function overriding 33
Objektum-orientált programozás Más gondolkozást kíván meg a programozótól mint a struktúrális programozás Objektumok hierarchiájának megtervezése 34