TGH07 - Chytré stromové datové struktury Jan Březina Technical University of Liberec 5. dubna 2017
Prioritní fronta Datová struktura s operacemi: Odeber Minum (AccessMin, DeleteMin) - vrat prvek s minimálním kĺıčem a odeber ho z fronty Zmenši Kĺıč (DecreaseKey) - zmenši kĺıč daného prvku ve frontě Naplnění fronty - vytvoření fronty z dané množiny prvků, nebo operace přidání prvku
Minimální halda - obecně Podporuje operace: insert - vložení ohodnoceného prvku access-min - čtení minimálního prvku delete-min - odstranění minimálního prvku delete - odstranění prvku se známou polohou merge - spojení dvou hald decrease-key - zmenšení hodnoty prvku se známou polohou Podobně můžeme zavést i maximální haldu. Speciální případ prioritní fronta má alespoň operace decrease-key, access-min,delete-min.
Složitost různých implementací haldy Seznam Tříděné pole Binární halda Fibonačiho halda Vložení O(1) O(n) O(lg n) O(1) Odeber Min. O(n) O(1) O(lg n) O(lg n) Najdi Min. O(n) O(1) O(1) O(1) Odebrání O(1) O(n) O(lg n) O(lg n) Zmenšení Kĺıče O(1) O(n) O(lg n) O(1) Spojení O(1) O(n) O(m lg (n + m)) O(1) - amortizovaný čas, průměrný čas při opakování operací
Odbočka: amortizovaná složitost Odhad půměrné složitosti operace v nejhorším případě, při provedení sekvence velkého množství operací. Příklad: Automaticky rostoucí pole. Operace přidání prvku: pokud je pole plné, alokujeme dvojnásobnou velikost a provedeme kopii. Celková složitost 2 n operací: f(2 n ) = O(2 n ) + O(2 n 1 ) +f(2 n 1 ) = O(2 n 1 )+O(2 n 2 )+ = O(2 n ) }{{}}{{} kopie přidávání Amortizovaná (průměrná) složitost: O(1)
Triviální implementace minimální haldy Nesetříděné pole, seznam prvky uložené v netříděném poli vložení prvku na konec v čase O(1) zmenši kĺıč, jen změna kĺıče O(1) nalezení minima, průchod polem v čase O(n) odebrání minima, posun zbytku pole v nejhorším čase O(n) Setříděné pole prvky uložené v setříděném poli vložení/zatřídění prvku v čase O(n) zmenši kĺıč, nové zatřídění v čase O(n) nalezení minima, poslední prvek v čase O(1) odebrání minima, odebraní posledního prvku v čase O(1) Lze nějak spojit výhody obou přístupů? Částečně setříděné pole?
Binární halda - I Vlastnosti minimální binární haldy: Kořenový binární strom. Uložení v poli, potomci vrcholu i jsou 2i, 2i + 1 (indexace od 1) Rodič menší než jeho dva potomci. Zaplnění všech vrstev krom poslední (pole nemá díry). Příklad:
Operace Insert 1. Přidej prvek na konec poslední vrstvy (konec pole) 2. Porovnej prvek s rodičem a pokud jsou ve správném uspořádání, skonči. 3. Jinak prohod a pokračuj bodem 2)
Příklad
Operace insert - pseudo kód function Insert(a, A[1:n]) přidej a na konec haldy (zvětš haldu) DecreaseKey(n + 1, a, A[1:n+1]) function DecreaseKey(i, key, A[1:n]) assert key <= A[i] A[i] = key while i > 1 and A[P arent(i)] > A[i] do Swap(A[Parent(i)],A[i]) i=parent(i)
Operace delete 1. Prohod kořen s posledním prvkem. 2. Odstraň poslední prvek (zmenši pole). 3. Heapify: 3.1 Vyber minimum z trojce: kořen, levý potomek, pravý potomek 3.2 Pokud je to kořen, skonči. 3.3 jinak prohod kořen s menším potomkem proved Heapify na podstrom
Heapify Vytvoření haldy s kořenem i pokud podstromy už jsou haldami: Left(i) = 2i, Right(i) = 2i + 1 function Heapify (i,a[1 : n]) // najdi minimum z i,l,r min = i; l = Left(i); r = Right(i) if l < n && A[l] < A[min] then min = l if r < n && A[r] < A[min] then min = r if min i then Swap (A[i],A[min]) Heapify (min,a[1:n])
Operace Access-min, Delete-min function AccessMin(A[1:n]) return A[1] function DeleteMin(A[1:n]) Swap(A[n],A[1]); zmenši haldu na n 1; Heapify(1, A[1:n-1])
Vytvoření haldy function BuildHeap(A[1:n]) for i = n/2 down to 1 do Heapify(i,A[1 : n])
Heap sort - vlastnosti Heapify předpokládá, že potomci i mají korektní haldy a utvoří korektní haldu ve vrcholu i. Složitost je rovna výšce haldy tj. O(log n) Celková složitost první i druhé fáze je O(n log n). Výhody: O(n log n) worst case složitost Nevýhody: nevyužije již setříděné pole, špatné pro cache
Datová struktura pro disjunktní rozklad množiny Chceme udržovat rozklad nějaké množiny M o n prvcích na disjunktní podmnožiny M = M 1 M k. Podmnožiny jsou identifikovány reprezentujícími prvky x i M i = M(x i ). Operace: MAKE SET(x) - vytvoř jednoprvkovou komponentu FIND(x) - najdi podmnožinu ve které je prvek x UNION(a, b) - sjednot podmnožiny M(a) a M(b), pokud a a b jsou reprezentatnti.
Implementace pomocí indexů reprezentatnů Pro každý prvek x M k držíme index repr[x] reprezentanta x k. Na počátku je repr[x] = x. FIND(x) vrací uložený index reprezentanta; čas O(1), UNION(a,b) - projít všechny prvky x M, - pro repr[x]==b nastavit repr[x]=repr[a]; čas O(n).
Implementace pomocí seznamů komponent Každá komponenta je spojový seznam, pro každý prvek x M k držíme index p[x] předka v seznamu. Na počátku je repr[x] = p. Dále držíme indexy last[x] posledního prvku v komponentě M(x) pro reprezentanta x. FIND(x) prochází seznam, najde první prvek; čas O(n), UNION(a,b) spojí dva seznamy; čas O(1).
Pozorování Jak spojit výhody obou přístupů? Komponenty nemusí tvořit seznam, ale libovolný zakořeněný strom. Tj. UNION nemusí připojovat jednu komponentu na konec druhé, ale přímo na kořen druhé. Update ukazatelů komponenty provedeme, až když poprvé potřebujeme jejich kořen. Optimalizace výšky stromů.
pomocí stromů - Find function Find (x) if x.parent == NULL then return x else x.parent = Find (x.parent) return x.parent Každá množina je strom s kořenem v reprezentantovi s orientovanými hranami (ukazateli) vedoucími ke kořeni. FIND - jde od listu ke kořeni. O(výška stromu) Všechny procházené prvky odkáže přímo na reprezentanta. (Komprese cesty.)
pomocí stromů - Union function Union (xroot, yroot) if xroot.rank > yroot.rank then yroot.parent = xroot else if xroot.rank < yroot.rank then xroot.parent = yroot else if xroot yroot then yroot.parent = xroot xroot.rank = xroot.rank + 1 UNION - kvuli vyvážení připojíme menší strom pod větší, výška vzroste o 1 jen při shodě výšek To samo zajistí FIND O(log n). Dohromady: FIND s amortizovanou složitostí O(α(n))
Ackermanova funkce α(n) je inverze n(α) = A(α, α) extrémně rostoucí Ackermannovy funkce, prakticky je α < 4.