www.www.zaachi.com » Blog/Algoritmy » Řadící algoritmy #1

Jedná se o takový algoritmus, který je schopný ze vstupního souboru dat vytvořit jejich posloupnost v předem určených podmínkách. Každý algoritmus se snaží najít takovou posloupnost, pro kterou platí, že předchozí prvek je menší nebo roven a následující prvek je větší nebo roven k aktuálnímu prvku, dle kritérií, které řazení určují.
Pro řazení existuje spousta hotových algoritmů, které se liší v mnoha aspektech, jako je složitost, stabilita, nebo metoda pro řazení.
Od těchto vlastností se odvíjí také požadavky, které jsou na algoritmy kladeny:
Paměťová náročnost
je mnohdy podceňovaným faktorem a je pravda, že v některých případech není až tak důležitá (od toho se odvíjí výběr správného altoritmu), ale ve většině případů je právě tento požadavek velmi
důležitý, hlavně při řazení velkého počtu záznamů.
Rychlost altoritmu, nebo její časová složitost, je ovlivňována počtem operací, které je nutno se vstupním souborem provést a je ovlivněna i počtem prvků, které jsou ve vstupním souboru dat. Většinou je tato složitost kvadratická, logaritmická nebo lineární, dle výběru algoritmu.
Délka kódu není až tak důležitý faktor, ale v praxi většinou platí, že čím kratší kód, tím efektivnější zpracování vstupu. Ovšem není to podmínkou.
Stabilita určuje, zda budou seřazené prvky souhlasit s jejích klíči. Většinou jsou při řazení využívány klíče a hodnoty, které se řadí závisle nebo nezávisle na sobě.
Přirozenost je vlastnost, kdy algoritmus je přirozený v případě, že čas řazení již z části seřazené vstupní posloupnosti je menší než čas pro řazení neseřazené posloupnosti.
Podle využívání paměti jsou algoritmy ještě členěny na vnitřní a vnější. Při použití vnitřního algoritmu je nutné, aby byla všechna data načtena a uložena v operační paměti, kam k nim algoritmus přistupuje Vnější algoritmy se využijí zejména při řazení velkého množství dat, kdy není možné všechna data mít načtená a načítá se jenom část dat, které se postupně řadí.
Bubble sort je jeden ze základních algoritmů řazení. Princip spočívá v porovnávání vždy dvou prvků, kdy vstupní soubor dat procházíme od počátku ke konci a dle podmínek řazení vždy tyto dva prvky prohazujeme nebo necháváme netknuté.
Nevýhoda spočívá v tom, že abychom dostali seřazenou posloupnost, je nutné celý vstupní soubor dat projít tolikrát, kolik obsahuje hodnot, od čehož se odvíjí právě velká časová náročnost tohoto algoritmu.
Název Bublinkové řazení (Bubble sort) vznikl právě z principu algoritmu, kdy největší prvky v souboru dat postupně při každém dalším procházení probublávají směrem doprava (dle použitého algoritmu).
Ukázka jednoduchého Bubble sortu v Javě, pro seřazení pole celých čísel, může vypadat takto:
//pomocna promenna
int tmp;
//v cyklku projdeme cele pole
for (int i = 0; i < InArray.length - 1; i++) {
for (int j = 0; j < InArray.length - i - 1; j++){
//porovname dve sousedni hodnoty
if (InArray[j + 1] < InArray[j]) {
/*
V pripade, ze nasledujici hodota
je mensi nez predchozi, tyto dve hodnoty prohodime
*/
//do pomocne promenne ulozime puvodni hodnotu
tmp = InArray[j];
//druhou hodnotu posuneme na prvni pozici
InArray[j] = InArray[j + 1];
//na druhou hodnotu ulozime puvodn prvni hodnotu
InArray[j + 1] = tmp;
}
}
}
//vracime jiz serazene pole
return InArray;
}
I když je po principiální stránce algoritmus dost pomalý, jde na internetu najít spoustu různých upravených alternativ, které jej vylepšují.
Existuje ještě jedno využití tohoto algoritmu, které je na rozdíl od řazení jedno z nejrychlejších. Jedná se o případ, kdy chceme zjistit, zda je vstupní soubor dat již seřazený nebo nikoli. Pro toto zjištění nám stačí jenom jeden průchod všech vstupních hodnot, při kterém porovnáváme vždy hodnoty dvou, vedle sebe, ležících prvků.
Ukázka takové funkce může v Javě vypadat takto:
public boolean IsSorted_BubbleSort( int[] InArray){
// v jednom cyklu projdeme celé pole
for( int i = 0; i < InArray.length - 1; i++ ){
//pokud je následující prvek menší než předchozí,
//vracíme false
if( InArray[ i ] > InArray[ i+1 ] )
return false;
}
//vracíme true
return true;
}
Přirozený: Ano
Stabilní: Ano
Minimální časová složitost: O(n)
Průměrná časová složitost: O(n^2)
Maximální časová složitost: O(n^2)
Insertion sort je další z algoritmů, který je založen na porovnávání hodnot, podobně jako Bubble sort, ovšem princip je zcela odlišný.
Algoritmus pracuje tak, že prochází neseřazenou strukturu, a každý aktuálně zpracovávaný prvek přesune na jeho konečné místo (některou z konců struktury), kam patří.
Jak vyplívá z podstaty, jeho časová složitost se přímo odvíjí od struktury vstupních dat. Pokud jsou data částečně seřazena, je algoritmus velmi rychlí. Stejně tak je velmi rychlí pro malé obsahy vstupních dat.
Od principu řazení se opět odvíjí název – řazení vkládáním (Insertion sorting), kdy se vstupní prvky struktury přímo vkládají na místo kam patří.
Další výhodou algoritmu je, že dokáže řadit „online“. To znamená, že umožňuje seřadit i předem nenačtenou strukturu, ale hodnoty můžeme do struktury postupně přidávat. Algoritmus nemusí předem znát celkový počet prvků. Takže umožňuje do seřazené struktury velmi jednoduše vložit další prvek a celou strukturu vrátit opět seřazenou.
Ukázkový kód v Javě, pro seřazení pole celých čísel, může vypadat takto:
public int[] InsertingSort(int[] InArray ) {
//v cylku projdeme cele pole od druheho prvku
//prvni prvek by nebylo kam presouvat
for (int i = 1; i < InArray.length; i++ ) {
//ulozime si hodnoty aktualniho prvku
int value = InArray[ i ];
//ulozime si pozici predchoziho prvku
int n = i - 1;
//vsechny prvni posuneme o jednu pozici doprava
//az do mista, na ktere patri nas prvek
while( ( n >= 0 ) && ( value < InArray[ n ] ) ){
InArray[ n + 1 ] = InArray[ n ];
--n;
}
//umistime nas aktualne zpracovavany prvek
InArray[ n + 1 ] = value;
}
//vracime serazene pole
return InArray;
}
Přirozený: Ano
Stabilní: Ano
Minimální časová složitost: O(n)
Průměrná časová složitost: O(n^2)
Maximální časová složitost: O(n^2)
Selection sort je principiálně velmi jednoduchý algoritmus. Cílem je postupně procházet celou strukturu vstupních hodnot, ve kterých se hledá vždy nejmenší prvek (nebo největší prvek, dle užití algoritmu), který se posune na konec struktury (nebo na začátek). Tímto postupem se nám postupně zmenšuje počet hodnot, ve kterých je nutné vyhledávat, a na konci (nebo začátku) struktury nám vzniká již seřazená posloupnost.
Nevýhodou je, že i když je na vstupu již částečně seřazená struktura, algoritmus to nerozlišuje a jeho složitost a časová náročnost je pořád stejná, protože se postupně zpracovávají všechny prvky. Název je opět odvozen od principu, protože algoritmus projde celé pole a vybere v něm jeden, vyhovující, prvek, který zařadí na jedenu z krajních pozic. Při každém průchodu se počet hodnot zmenšuje o jednu (n-1) a tento postup se opakuje, dokud nejsou všechny prvky vyčerpány (n = 0).
Opět jednoduchá ukázka v Javě, pro seřazení pole celých čísel, může vypadat takto:
public int[] SelectionSort(int[] InArray) {
int key;
// v cylku projdeme cele pole
for (int i = 0; i < InArray.length - 1; i++) {
//hledame minimum v poli
//nejmensi hodnotu si nastavime jako aktualni hodnotu
key = i;
//v cylku projdeme od dalsi hodnoty
for( int j = i + 1; j < InArray.length; j++ ){
//porovnavame, zda je hodnota mensi nez naze aktualne ulozena hodnota v key
if( InArray[ j ] < InArray[ key ] )
//pokud ano, nastavime ji jako aktualni
key = j;
}
//zaradime hodnotu na spravne misto
int tmp = InArray[ i ];
InArray[ i ] = InArray[ key ];
InArray[ key ] = tmp;
}
//vracime serazene pole
return InArray;
}
Přirozený: Ne
Stabilní: Ano
Časová složitost: O(n^2)
Toto jsou tři nejjednodušší řadící algoritmy, se kterými se můžete v praxi setkat. Jejich implementace nemusí být nutně taková, jak je zde uvedeno, a všechny tyto algoritmy je možné libovolně upravit a změnit tak některou jejich vlastnost, většinou na úkor vlastnosti jiné. V příštím článku se podíváme na další, složitější, řadící algoritmy.
