# Tabulka - vztah mezi množinou klíčů a množinou hodnot (jako například slovník) - asociativní abstraktní datová struktura ### Klíč - diskrétní datový typ: - `int` - `String` - ne `double`! - nejdůležitější vlastnost - možnost zjistit, zda jsou si dvě hodnoty rovny - při vhodné implementaci vyhodnocení rovnosti může být klíčem i složitější struktura (např. množina čísel $\{0, 1, 2\}$ je shodná s $\{2, 0, 1\}$, může být tedy klíčem) ### Hodnota - libovolný datový typ nebo reference na instanci ### Vlastnosti - není určeno pořadí prvků - některým klíčům nemusí být přiřazena žádná hodnota ### Operace - přidání přiřazení (klíč + hodnota) - získání hodnoty pro daný klíč - zjištění existence hodnoty přiřazené danému klíči - zrušení přířazení (odebrání klíče) - získání všech klíčů s přiřazenou hodnotou Ideálně by měly mít všechny (kromě všech klíčů) složitost $\Theta(1)$ ### Implementace tabulky **Tabulka s přímým adresováním** - pole, kde index je klíč - problém: - často neznáme rozsah možnách klíčů - počet přípustných klíčů může být velmi velký nebo nekonečný - neřeší složitější klíče - výhoda: splňuje $\Theta(1)$ **Reprezentace absence prvku** - pomocí obalovací třídy (např. `Element`) - ta obsahuje `value` s daty - odlišíme tak neexistující klíč od `null`, `-1`, ... **Rozptylová tabulka** (hash table) - hashovací funkce - vstup: *klíč*, výstup: *index v poli* - vlastnosti: - hodnota 0 až délka pole -1 - pro různé hodnoty různé klíče - nelze vždy, počet klíčů může být větší než délka pole - pro stejný klíč vrátí stejný index **Kolize** - vložíme hodnotu s klíčem $k_1$, uloží se na index $H(k_1)$ - vložíme další hodnotu s takovým klíčem $k_2$, že $H(k_1) = H(k_2)$ - pozice v poli už je obsazena - nedá se jim dost dobře bránit, musí se řešit - do pole uložíme spojové struktury - každý záznam v poli obsahuje hodnotu i klíč - záznam bude obsahovat `next`, kam se dá případný další klíč s hodnotou ![](implementace-tabulky.png) ### Implementace operací - přidání prvku - vložíme jej na začátek spojové struktury (nemusíme jí celou procházet) - získání prvku - procházíme `while` cyklem - porovnáváme klíče - jeho instance musí být schopna rozhodnout o své významové shodnosti (`.equals()`) ### Výpočetní složitost - přidání prvku: $\Theta(1)$ - vybrání a odebrání prvku závisí na délce spojové struktury - přidáno bylo $n$ prvků - používá se pole o délce $m$ - složitost tedy je $\Theta(n/m) = \Theta(n)$ - **řešení**: - omezit poměr $n/m$ (např. $n/m < 10$) - po překročení limitu se data přesunou do větší struktury - průměrná složitost je poté $\Theta(1)$ **Rozložení klíčů** - známe-li rozložení klíčů, je potřeba mu přizpůsobit hashovací funkci - pokud ne, funkce by měla záviset na všech částech klíče - ne jen na prvním písmenu, poslední číslici, ... ### Implementace v Javě - třída `HashMap` - typové parametry `` (klíč) a `` (hodnota) **Metody** - `void put(K key, V value)` - `V get(K key)` - `boolean containsKey(K key)` - v průměrném případě $\mathcal O(1)$ **Rozptylová funkce** - používá se `public int hashCode() {...}` na datovém typu - výsledek může být libovolné číslo (java se postará o namapování na indexy pole) **Rovnost klíčů** - používá se `boolean equals(Object o) {...}` Při implementaci `equals` a `hashCode` musíme dbát na jejich konzistenci! - když `a.equals(b) == true`, pak `a.hashCode() == b.hashCode()` - naopak to neplatí - `a.equals(a)` musí vracet `true` - když `a.equals(b) == true`, potom musí `b.equals(a) == true` **Třída `HashSet`** - má pouze klíče - hodí se k efektivnímu nalezení duplicit