TGH07 - Chytré stromové datové struktury Jan Březina Technical University of Liberec 1. dubna 2014
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í
Amortizovaná složitost - dynamické pole
Amortizovaná složitost - přičítání jedničky Kolik je v posloupnosti 1,..., 2 k čísel s l 1 jedničkami na konci? Pro l = 1 jich je 2 k 1. Obecně 2 k l. Celkový počet přístupů k jednomu bitu: T (2 k ) = k l2 k l = (2 k 1)+(2 k 1 1)+... (2 k k 1) = 2 k+1 k 1 l=1 amortizovaný počet přístupů na jednu operaci je T (2 k ) 2 k 2.
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]) if key > A[i] then error: nový kĺıč menší než aktuální; ; 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]) Má lineární!! složitost. Viz. přičítání jedničky.
Heap sort Algoritmus: // make maximal heap; BuildHeap(A[1:n]); for i = n down to 1 do DeleteMax(A[1 : i]) 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: nestabilní sort; 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(x,y) - sjednot podmnožiny M(x) a M(y)
Dvě implementace pomocí poĺı Spojové seznamy komponent Každá množina je spojový seznam, každá prvek má ukazatel na další, pro každou komponentu držíme ukazatel na první a poslední prvek (x i ). M i : a b... x i x i FIND prochází seznam, čas O(n), UNION spojí dva seznamy, čas O(1). Přímé ukazatele na reprezentanty Každý prvek ukazuje přímo na reprezentanta x i svojí komponenty. FIND - vrací svůj ukazatel na reprezentanta, čas O(1), UNION(a,b) - musí projít všechny prvky komponenty b a odkázat je na x a, čas O(n). Jak spojit výhody obou?
Pozorování 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.