Quicksort je velmi rychlý nestabilní řadící algoritmus na principu Divide et Impera (rozděl a panuj) s asymptotickou složitostí a s očekávanou složitostí
. Algoritmus vymyslel v roce 1962 Sir Charles Antony Richard Hoare.
Princip
Zvolme v zadaném poli libovolný prvek a říkejme mu pivot. Nyní můžeme pole přeházet tak, aby na jedné straně byly prvky větší než pivot, na druhé menší než pivot a pivot samotný byl umístěn přesně mezi těmito částmi. Tento postup můžeme zopakovat pro obě rozdělené části (bez pivota, ten je již umístěn na správném místě). Proceduru opakujeme tak dlouho, dokud nenarazíme na všechny triviálně řešitelné podproblémy (pole velikosti 1). V tento okamžik je celé pole seřazeno od nejvyššího prvku.
Výkonnost algoritmu
Výkonnost quicksortu je dána především volbou dobrého pivota. Pokud jej volíme ideálně, tak dojde při každém rekurzívním volání k rozpůlení pole a vystačíme si tedy s voláními, v nichž popřehazujeme až
prvků. Složitost tohoto případu je proto
.
Na druhou stranu, pokud nemáme štěstí a pivota volíme špatně (tj. nejvyšší nebo nejnižší možný prvek), tak nedojde k žádnému dělení podproblému, pouze dojde vždy k zařazení pivota na správné místo. Při každém z volání procedury spotřebujeme až
operací na ověření pořadí, případně na přesun prvků. Složitost tohoto patologického případu je proto
.
Výkon na malých datech
Za povšimnutí stojí, že při menších velikostech řazeného pole je quicksort poměrně pomalý, protože velmi pravděpodobně nedojde rozdělení pole na ideální poloviny. Tuto situaci si můžeme ukázat na dělení podproblému (viz obrázek), ve které volíme za pivota číslo 4, ale úloha se zkrátí jen o 1.
Z tohoto důvodu se quicksort používá zejména při řazení velkých polí. Dílčí malé podproblémy (cca. ) je vhodné řešit pomocí jiných algoritmů - například
insertion sortu nebo Shell sortu, které jsou při malých velikostech polí výrazně rychlejší.
Volba pivota
Pro volbu pivota existuje mnoho strategií. Jednou z nich je volba fixního prvku (tj. prvního, posledního...). Tento postup je problematický na částečně uspořádaných polích, případně na polích s nějakou strukturou, kde nedochází k optimálnímu dělení problému a složitost narůstá až k . Tomuto chování se lze vyhnout volbou mediánu prvního, posledního a prostředního prvku řazeného úseku pole.
Druhou populární strategii je volba náhodného prvku. Zde je problémem již zajištění samotné náhodnosti volby, respektive opakující se vzory v chování generátoru čísel (v extrémním případě může strategie degradovat až k poněkud komplikovanější volbě fixního prvku). V praxi se ale ukazuje, že bohatě postačí i pseudonáhodné generátory.
Vizualizace
Kód
01.
procedure
quicksort(List values)
02.
if
values
.
size <=
1
then
03.
return values
04.
05.
pivot = náhodný prvek z values
06.
07.
Rozděl seznam values
do
3
seznamů
08.
seznam1 =
{ prvky větší než pivot }
09.
seznam2 =
{ pivot }
10.
seznam3 =
{ prvky menší než pivot }
11.
12.
return quicksort(seznam1) + seznam2 + quicksort(seznam3)
01.
/**
02.
* Quicksort - rychle razeni (pivotem je prvni prvek), radi od nejvyssiho prvku
03.
* @param array pole k serazeni
04.
* @param left index prvniho prvku, na ktery muzeme sahnout (leva mez (vcetne))
05.
* @param right index prvniho prvku, na ktery nemuzeme sahnout (prava mez (bez))
06.
*/
07.
public
static
void
quicksort(
int
[] array,
int
left,
int
right){
08.
if
(left < right){
09.
int
boundary = left;
10.
for
(
int
i = left +
1
; i < right; i++){
11.
if
(array[i] > array[left]){
12.
swap(array, i, ++boundary);
13.
}
14.
}
15.
swap(array, left, boundary);
16.
quicksort(array, left, boundary);
17.
quicksort(array, boundary +
1
, right);
18.
}
19.
}
20.
21.
/**
22.
* Prohodi prvky v zadanem poli
23.
* @param array pole
24.
* @param left prvek 1
25.
* @param right prvek 2
26.
*/
27.
private
static
void
swap(
int
[] array,
int
left,
int
right){
28.
int
tmp = array[right];
29.
array[right] = array[left];
30.
array[left] = tmp;
31.
}
01.
/**
02.
* Quicksort - rychle razeni (pivotem je prvni prvek), radi od nejvyssiho prvku
03.
* @param array pole k serazeni
04.
* @param left index prvniho prvku, na ktery muzeme sahnout (leva mez (vcetne))
05.
* @param right index prvniho prvku, na ktery nemuzeme sahnout (prava mez (bez))
06.
*/
07.
void
quicksort(
int
array[],
int
left,
int
right){
08.
if
(left < right){
09.
int
boundary = left;
10.
for
(
int
i = left + 1; i < right; i++){
11.
if
(array[i] > array[left]){
12.
swap(array, i, ++boundary);
13.
}
14.
}
15.
swap(array, left, boundary);
16.
quicksort(array, left, boundary);
17.
quicksort(array, boundary + 1, right);
18.
}
19.
}
20.
21.
/**
22.
* Prohodi prvky v zadanem poli
23.
* @param array pole
24.
* @param left prvek 1
25.
* @param right prvek 2
26.
*/
27.
void
swap(
int
array[],
int
left,
int
right){
28.
int
tmp = array[right];
29.
array[right] = array[left];
30.
array[left] = tmp;
31.
}
01.
/**
02.
* Quicksort - rychle razeni (pivotem je prvni prvek), radi od nejvyssiho prvku
03.
* @param array pole k serazeni
04.
* @param left index prvniho prvku, na ktery muzeme sahnout (leva mez (vcetne))
05.
* @param right index prvniho prvku, na ktery nemuzeme sahnout (prava mez (bez))
06.
*/
07.
public
static
void
Quicksort(
int
[] array,
int
left,
int
right)
08.
{
09.
if
(left < right)
10.
{
11.
int
boundary = left;
12.
for
(
int
i = left + 1; i < right; i++)
13.
{
14.
if
(array[i] > array[left])
15.
{
16.
Swap(array, i, ++boundary);
17.
}
18.
}
19.
Swap(array, left, boundary);
20.
Quicksort(array, left, boundary);
21.
Quicksort(array, boundary + 1, right);
22.
}
23.
}
24.
25.
/**
26.
* Prohodi prvky v zadanem poli
27.
* @param array pole
28.
* @param left prvek 1
29.
* @param right prvek 2
30.
*/
31.
private
static
void
Swap(
int
[] array,
int
left,
int
right)
32.
{
33.
int
tmp = array[right];
34.
array[right] = array[left];
35.
array[left] = tmp;
36.
}