3.9 KiB
3.9 KiB
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 indexH(k_1)
- vložíme další hodnotu s takovým klíčem
k_2
, žeH(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 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()
)
- procházíme
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)
- omezit poměr
- přidáno bylo
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<K, V>
- typové parametry
<K>
(klíč) a<V>
(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
, paka.hashCode() == b.hashCode()
- naopak to neplatí
a.equals(a)
musí vracettrue
- když
a.equals(b) == true
, potom musíb.equals(a) == true
Třída HashSet<T>
- má pouze klíče - hodí se k efektivnímu nalezení duplicit