Rychlé řazení (quicksort)

  • Informatika
  • rychlé řazení quicksort
  • quicksort vysvětlení
  • algoritmus quicksort
  • rychlé řazení složitost
  • lomutovo dělení
  • volba pivota quicksort

Podcast

Algoritmus rychlého řazení (Quicksort)0:00 / 9:37
0:001:00 zbývá

Rychlé řazení (Quicksort): Podrobný průvodce algoritmem

Rychlé řazení, anglicky známé jako Quicksort, je jedním z nejefektivnějších a nejpoužívanějších řadicích algoritmů. Byl vyvinut Tonym Hoarem v roce 1959 a publikován v roce 1961. Tento algoritmus spadá do kategorie „rozděl a panuj“ (divide and conquer) a je ceněn pro svou rychlost v průměrném případě a relativně snadnou implementaci. Quicksort pracuje přímo v paměti (in-place), což minimalizuje potřebu dodatečné paměti.

Díky své efektivitě se široce uplatňuje v mnoha oblastech, od databázových systémů po operační systémy. Pochopení Quicksortu je klíčové pro studenty informatiky a každého, kdo se zabývá efektivním zpracováním dat.

Co je to Rychlé řazení (Quicksort) a jak funguje?

Rychlé řazení funguje na principu rekurzivního rozdělení velkého problému na menší, lépe řešitelné podproblémy. Cílem je efektivně uspořádat prvky v poli.

Princip algoritmu „rozděl a panuj“ v Quicksortu

Základní myšlenka spočívá v opakovaném dělení a řazení. Celý proces Quicksortu lze rozdělit do tří hlavních kroků:

  1. Vyber pivot: Z pole prvků, které mají být seřazeny, je vybrán jeden prvek, tzv. „pivot“. Ten slouží jako referenční bod pro rozdělení pole.
  2. Rozděl pole (partition): Ostatní prvky pole jsou přeskupeny tak, že všechny prvky menší než pivot jsou umístěny před ním a všechny prvky větší než pivot jsou umístěny za ním. Prvky rovné pivotu mohou být na libovolnou stranu. Po tomto kroku je pivot na své konečné seřazené pozici.
  3. Rekurzivně seřaď podpole: Algoritmus je rekurzivně aplikován na podpole prvků menších než pivot a na podpole prvků větších než pivot. Základní případ rekurze nastává, když podpole obsahuje nula nebo jeden prvek, které jsou již považovány za seřazené.

Detailní pohled na volbu pivota v Quicksortu

Volba pivota má zásadní dopad na výkon rychlého řazení. Špatná volba může vést k nejhoršímu případu časové složitosti. Existuje několik běžných strategií pro výběr pivota.

Strategie volby pivota a jejich vliv na rychlé řazení

  • První nebo poslední prvek: Toto je nejjednodušší volba. Pokud je vstupní pole již seřazené nebo téměř seřazené, tato volba vede k nejhoršímu případu složitosti. Pivot je totiž vždy nejmenší nebo největší prvek, což způsobuje nevyvážené dělení.
  • Náhodný prvek: Náhodný výběr pivota pomáhá zabránit nejhoršímu případu pro specifické vstupní datové sady. Nezaručuje sice průměrný výkon, ale výrazně zvyšuje pravděpodobnost, že se algoritmus bude chovat jako v průměrném případě.
  • Medián ze tří: Tato strategie vybere medián z prvního, středního a posledního prvku podpole. Je považována za dobrou rovnováhu mezi jednoduchostí a výkonem. Snižuje pravděpodobnost výběru extrémního prvku jako pivota a zlepšuje šance na vyvážené dělení.
  • Medián z více prvků: Jedná se o obecnější verzi mediánu ze tří. Vybere se medián z většího počtu prvků (např. z pěti nebo devíti). Tato metoda dále zvyšuje pravděpodobnost dobrého pivota, ale za cenu vyšší režie při jeho výběru.

Ověř si, co už víš

Test · 20 otázek · první 3 zdarma

Otázka 1 z 30%

Po rekurzivním seřazení podpolí v algoritmu Quicksort je nutné provést dodatečný krok sloučení pro spojení výsledků.

Procvič si to na kartičkách

20 kartiček · první 3 zdarma bez účtu

1 / 20

Co je Quicksort?

Quicksort je jeden z nejefektivnějších a nejčastěji používaných řadicích algoritmů, vynalezený Tonym Hoarem v roce 1959. Patří do kategorie algoritmů

Klepni pro otočení · Swipni pro navigaci

Lomutovo dělení: Praktický příklad v rychlém řazení

Lomutovo dělení je jedním z často popisovaných algoritmů pro rozdělení pole. Jeho hlavním cílem je umístit pivot na správnou pozici v seřazeném poli a zároveň přesunout všechny menší prvky nalevo a větší prvky napravo od něj.

Vysvětlení Lomutova dělení krok za krokem

Předpokládejme, že pivot je vybrán jako poslední prvek v aktuálním podpoli A[k]...A[m]. Algoritmus využívá dva ukazatele:

  • i: Ukazatel na konec „menší“ části pole (prvky menší nebo rovné pivotu).
  • j: Ukazatel, který prochází polem od začátku do předposledního prvku (před pivotem).

Kroky Lomutova dělení:

  1. Vyberte pivot, například poslední prvek v podpoli. Pro zjednodušení ho můžeme vyměnit s A[m]. Nechť pivot = A[m].
  2. Inicializujte ukazatel i na k - 1 (nebo start_index - 1). Tento ukazatel sleduje hranici mezi prvky menšími než pivot a prvky většími než pivot.
  3. Procházejte polem od j = k do m - 1 (nebo start_index do end_index - 1):
  • Pokud je A[j] ≤ pivot, inkrementujte i a vyměňte A[i] a A[j]. Tím se A[j] přesune do „menší“ části pole.
  1. Po dokončení cyklu vyměňte A[i + 1] a A[m] (pivot). Tím se pivot umístí na svou konečnou pozici.
  2. Vraťte index i + 1, což je konečná pozice pivota.

Příklad Lomutova dělení pro lepší pochopení

Mějme pole [7, 2, 1, 6, 8, 5, 3, 4] a chceme seřadit podpole od indexu 0 do 7. Zvolíme poslední prvek 4 jako pivot.

  • Počáteční stav: A = [7, 2, 1, 6, 8, 5, 3, 4], pivot = 4, i = -1, j začíná na 0.
  1. j = 0, A[0] = 7. 7 > 4. Nic se neděje. (A = [7, 2, 1, 6, 8, 5, 3, 4], i = -1)
  2. j = 1, A[1] = 2. 2 ≤ 4. Inkrementujeme i na 0. Vyměníme A[0] a A[1]. Pole je [2, 7, 1, 6, 8, 5, 3, 4]. (A = [2, 7, 1, 6, 8, 5, 3, 4], i = 0)
  3. j = 2, A[2] = 1. 1 ≤ 4. Inkrementujeme i na 1. Vyměníme A[1] a A[2]. Pole je [2, 1, 7, 6, 8, 5, 3, 4]. (A = [2, 1, 7, 6, 8, 5, 3, 4], i = 1)
  4. j = 3, A[3] = 6. 6 > 4. Nic se neděje. (A = [2, 1, 7, 6, 8, 5, 3, 4], i = 1)
  5. j = 4, A[4] = 8. 8 > 4. Nic se neděje. (A = [2, 1, 7, 6, 8, 5, 3, 4], i = 1)
  6. j = 5, A[5] = 5. 5 > 4. Nic se neděje. (A = [2, 1, 7, 6, 8, 5, 3, 4], i = 1)
  7. j = 6, A[6] = 3. 3 ≤ 4. Inkrementujeme i na 2. Vyměníme A[2] a A[6]. Pole je [2, 1, 3, 6, 8, 5, 7, 4]. (A = [2, 1, 3, 6, 8, 5, 7, 4], i = 2)

Cyklus končí. Nyní vyměníme A[i + 1] (tj. A[3]) s pivotem A[7]. Výsledné pole je [2, 1, 3, 4, 8, 5, 7, 6].

Pivot 4 je nyní na indexu 3. Všechny prvky nalevo (2, 1, 3) jsou menší nebo rovny 4. Všechny prvky napravo (8, 5, 7, 6) jsou větší než 4. Algoritmus nyní rekurzivně seřadí podpole [2, 1, 3] a [8, 5, 7, 6].

Složitost rychlého řazení (Quicksort): Analýza výkonu

Výkon rychlého řazení se typicky posuzuje pomocí časové složitosti, která udává, jak se mění doba běhu algoritmu s rostoucí velikostí vstupních dat (n).

Časová a prostorová složitost Quicksortu

  • Průměrná časová složitost: O(n log n) V průměrném případě, kdy volba pivota vede k relativně vyváženému dělení pole, dosahuje rychlé řazení vynikajícího výkonu. Každé dělení pole trvá O(n) času. Pokud se pole dělí zhruba na poloviny, je potřeba log n úrovní rekurze. Celková složitost je pak součinem těchto dvou faktorů, tedy O(n log n). Toto je typický výkon pro většinu náhodných vstupů a je považován za optimální pro srovnávací řadicí algoritmy.

  • Nejhorší časová složitost: O(n²) Nejhorší případ nastává, když volba pivota vždy vede k extrémně nevyváženému dělení. To se stane, pokud je pivotem vždy nejmenší nebo největší prvek v podpoli (například, pokud je pole již seřazené a pivot je vždy vybrán jako první prvek). V takovém případě jedno podpole obsahuje n-1 prvků a druhé 0 prvků. Každý dělicí krok stále trvá O(n) času, ale je potřeba n úrovní rekurze. Celková složitost je pak O(n · n) = O(n²). Algoritmy jako Merge Sort nebo Heap Sort mají garantovanou složitost O(n log n) i v nejhorším případě, což je jejich výhoda oproti Quicksortu v těchto specifických scénářích.

  • Prostorová složitost: Quicksort je in-place algoritmus, což znamená, že k řazení nepotřebuje mnoho dodatečné paměti. Prostorová složitost je dána hloubkou rekurzního zásobníku. V průměrném případě je to O(log n), v nejhorším případě O(n) (pokud je rekurze velmi hluboká kvůli nevyváženému dělení).

Pseudokód a implementace Quicksortu

Základní struktura rychlého řazení je elegantně rekurzivní.

Struktura algoritmu v pseudokódu

funkce quicksort(pole, začátek, konec):
 pokud začátek < konec:
 // Rozděl pole a získej index pivota
 index_pivota = rozdel_pole(pole, začátek, konec)
 // Rekurzivně seřaď levé podpole
 quicksort(pole, pole, začátek, index_pivota - 1)
 // Rekurzivně seřaď pravé podpole
 quicksort(pole, index_pivota + 1, konec)

funkce rozdel_pole(pole, začátek, konec):
 // Zvol pivot (např. poslední prvek)
 pivot = pole[konec]
 i = začátek - 1
 // Procházej polem a přeskupuj prvky
 pro j od začátek do konec - 1:
 pokud pole[j] <= pivot:
 i = i + 1
 vyměň(pole[i], pole[j])
 // Umísti pivot na správnou pozici
 vyměň(pole[i + 1], pole[konec])
 // Vrať index pivota
 návrat i + 1

Běžné chyby a úskalí při použití rychlého řazení (Quicksort)

I přes svou efektivitu má Quicksort několik úskalí, na která je třeba si dát pozor.

Na co si dát pozor při implementaci Quicksortu

  • Špatná volba pivota: Konzistentní výběr špatného pivota (např. vždy nejmenšího nebo největšího prvku) vede k nejhoršímu případu O(n²). Tomu lze předejít použitím strategií jako „medián ze tří“ nebo náhodným výběrem.
  • Problémy s malými poli: Pro velmi malá podpole (např. méně než 10-20 prvků) je režie rekurze a dělení Quicksortu vyšší než u jednodušších algoritmů, jako je řazení vkládáním (Insertion Sort). Hybridní přístupy často přepínají na Insertion Sort pro malá podpole, aby se zlepšil celkový výkon.
  • Nestabilita: Quicksort je nestabilní řadicí algoritmus. To znamená, že pokud existují dva prvky se stejnou hodnotou, jejich relativní pořadí v seřazeném poli nemusí být zachováno. Pro některé aplikace je stabilita důležitá, a v takových případech je třeba zvolit jiný algoritmus (např. Merge Sort).
  • Přetečení zásobníku: Hluboká rekurze v nejhorším případě může vést k přetečení zásobníku (stack overflow), pokud není implementována s optimalizací koncového volání nebo iterativně.

Jak simulace pomáhá pochopit rychlé řazení (Quicksort)

Interaktivní simulace rychlého řazení je skvělý nástroj pro vizuální pochopení jeho fungování. Studenti mohou v simulaci pozorovat a experimentovat s několika klíčovými aspekty.

Pozorování a experimenty v simulaci Quicksortu

  • Krokování dělení podle pivota: Nejdůležitější je sledovat, jak se pole dělí v každém kroku. Simulace umožňuje vizualizovat pohyb ukazatelů i a j (v případě Lomutova dělení) a výměny prvků. Můžete si všimnout, jak se prvky menší než pivot postupně shromažďují na levé straně a větší na pravé. To pomáhá pochopit, proč je pivot po dělení na své konečné pozici.
  • Vliv volby pivota: Změnou strategie volby pivota (např. první prvek, náhodný, medián ze tří) lze pozorovat, jak se mění průběh dělení a celkový počet porovnání a výměn. Můžete experimentovat s již seřazenými, obráceně seřazenými nebo náhodnými daty a vidět, jak se algoritmus chová v průměrném a nejhorším případě.
  • Rekurzivní volání: Simulace často vizualizuje rekurzivní volání na podpole. Lze sledovat, jak se algoritmus „ponořuje“ do menších podpolí a jak se po jejich seřazení vrací zpět.
  • Porovnání s jinými algoritmy: Některé simulace umožňují porovnat rychlé řazení s jinými algoritmy (např. řazení bublinkové, řazení výběrem). To pomáhá studentům pochopit, proč je Quicksort v průměru mnohem rychlejší.

Experimentováním s různými vstupními daty a strategiemi volby pivota v simulaci si studenti mohou lépe upevnit znalosti o tom, jak Quicksort funguje, jaké jsou jeho silné stránky a slabiny a jak optimalizovat jeho výkon. Je to klíčové pro hlubší pochopení konceptu „rozděl a panuj“ a jeho praktických důsledků.

Nejčastější dotazy studentů k rychlému řazení (FAQ)

Co je to rychlé řazení Quicksort?

Rychlé řazení (Quicksort) je efektivní řadicí algoritmus, který funguje na principu „rozděl a panuj“. Vybere pivot, rozdělí pole na prvky menší a větší než pivot, a pak rekurzivně řadí tato podpole. Je oblíbený pro svou rychlost v průměrném případě.

Proč je volba pivota u Quicksortu tak důležitá?

Volba pivota je klíčová, protože ovlivňuje, jak vyvážené bude rozdělení pole. Špatná volba (např. vždy nejmenší nebo největší prvek) může vést k nevyváženým podpolím a způsobit nejhorší časovou složitost O(n²). Dobrá volba (např. medián ze tří) zajišťuje průměrnou složitost O(n log n).

Jaká je složitost rychlého řazení Quicksort?

Průměrná časová složitost Quicksortu je O(n log n), což je velmi efektivní. V nejhorším případě, při špatné volbě pivota, může klesnout na O(n²). Prostorová složitost je v průměru O(log n), ale v nejhorším případě O(n) kvůli rekurzní hloubce.

Je rychlé řazení stabilní algoritmus?

Ne, rychlé řazení (Quicksort) je nestabilní řadicí algoritmus. To znamená, že pokud jsou v poli dva prvky se stejnou hodnotou, jejich původní relativní pořadí se v seřazeném poli nemusí zachovat. Pro stabilní řazení by bylo potřeba použít jiný algoritmus, například Merge Sort.

Kdy je vhodné použít Quicksort?

Quicksort je vhodný pro řazení velkých datových sad, kde je průměrný výkon klíčový. Je ideální pro situace, kdy je důležitá rychlost a místo (in-place řazení) a kdy stabilita řazení není prioritou. Je často používán v knihovních implementacích, například v qsort funkci v jazyce C.

Procvič si to, ne jen přečti

20 kartiček, test s vysvětlením a podcast k tomuto tématu. Účet je zdarma a pokračuješ tam, kde jsi skončil.

  • Bez platební karty
  • Web i mobilní aplikace
  • 330 000+ studentů
Začni zdarma

Související témata