dynamic array
Dynamické pole je datová struktura, která se navenek chová jako běžné pole (přístup k prvkům v čase $O(1)$), ale nemá fixní velikost. Dokáže automaticky měnit svou kapacitu podle toho, kolik prvků do něj vkládáme nebo z něj mažeme.
Implementace ve standardních knihovnách:
- C++ -
std::vector - Java -
ArrayList - Python -
list
Rozlišujeme dvě důležité hodnoty:
- velikost - kolik prvků je v poli skutečně uloženo
-
kapacita- kolik paměti je pro pole alokováno
Zvětšování
Když chceme přidat prvek (operace
push_backneboappend), ale pole je plné (size == capacity), musíme ho zvětšit.
Algoritmus:
- Alokujeme nové pole, které je větší (typicky 2× větší než původní).
- Zkopírujeme všechny prvky ze starého pole do nového.
- Přidáme nový prvek.
- Staré pole smažeme z paměti.
Zmenšování
Když prvky odebíráme (operace pop), chceme uvolnit paměť, pokud je pole příliš prázdné.
Naivní přístup (Chyba): Pokud bychom pole zmenšili na polovinu okamžitě, jakmile klesne zaplnění pod 50 %, narazíme na problém zvaný thrashing (kmitání).
- Představte si, že jste na hraně kapacity.
- Přidáte prvek -> Pole se zdvojnásobí (drahá operace).
-
Smažete prvek -> Pole je pod 50 %, zmenší se na polovinu (drahá operace).
- Přidáte prvek -> Znovu se zdvojnásobí… Pokud byste střídavě dělali
pushapopna této hraně, složitost by degradovala na $O(n)$.
Správný přístup (S čtvrtinovou rezervou): Pole zmenšíme na polovinu (halving) až tehdy, když počet prvků klesne na $1/4$ kapacity.
- Po zmenšení bude pole zaplněné z $1/2$.
- Máme tedy “bezpečnou zónu” (buffer), než bychom museli znovu zvětšovat.
Analýza časové složitosti
Složitost jedné operace v nejhorším případě (worst-case) je $O(n)$, protože musíme vše zkopírovat. Ale amortizovaná (průměrná) složitost je $O(1)$.
Použijme tzv. Bankéřovu metodu: Představme si, že každá operace stojí “peníze” (jednotky času). Kopírování prvku stojí $1. Vložení prvku stojí $1.
- Za každé vložení (append) zaplatíme $3:
- $1 zaplatí aktuální vložení prvku
- $2 si “uložíme na spořící účet” k tomuto prvku
Když se pole naplní (má velikost $N$) a musíme ho zdvojnásobit na $2N$:
- Musíme zkopírovat $N$ prvků. To stojí $N$ dolarů.
- Kde je vezmeme? Každý z těch $N$ prvků, které jsme tam vložili od posledního zvětšení, má u sebe naspořené $2 (nebo alespoň $1, záleží na přesné matematice). Tyto úspory použijeme na zaplacení kopírování.
- Díky tomu “drahá” operace kopírování nestojí nic navíc – byla předplacena levnými operacemi předtím.
Výsledek: Přidání prvku má amortizovanou složitost $O(1)$.