Merge sort (řazení sléváním)
Merge sort (řazení sléváním) je řadící algoritmus, založený na myšlence rekurzivního dělení pole na dvě poloviny a jejich následném spojování („slévání“) do neklesající posloupnosti. Byl popsán John von Neumannem v roce 1945. Merge sort je přirozený a stabilní.
Mezi výhody tohoto řadícího algorimu patří logaritmická časová složitost. Nevýhodou je nutnost alokace druhého pomocného pole o stejné velikosti, jako je pole řazené.
Základní kroky algoritmu:
- rekurzivní dělení pole na poloviny, až do triviálního případu (jeden prvek)
- jeden prvek se triviálně pokládá za seřazený
- zpětné spojení obou seřazených polovin do neklesající posloupnosti
Implementace (Java)
Uvedená implementace používá generické typy, takže je univerzální.
/**
* Implementace algoritmu merge sort.
* @param pole pole k seřazení
* @author Vojtěch Hordějčuk
*/
public static <T extends Comparable<? super T>> void mergeSort (final T pole[])
{
// vytvoř dočasné pole o stejné velikosti
T pracovni[] = new T[pole.length];
// spusť rekurzivní řazení pro celé pole
mergeSort (pole, pracovni, 0, pole.length - 1);
}
/**
* Hlavní metoda pro rekurzivní řazení.
* @param pole pole k seřazení
* @param pracovni pracovní pole o stejné velikosti
* @param prvni index prvního prvku
* @param posledni index poslední prvku
* @author Vojtěch Hordějčuk
*/
private static <T extends Comparable<? super T>> void mergeSort (final T pole[], final T pracovni[], int prvni, int posledni)
{
// řadit je nutné pouze část pole s více než jedním prvkem
if (prvni != posledni)
{
// vypočítej index středu
int stredni = (prvni + posledni) / 2;
// seřaď levou polovinu (včetně středu)
mergeSort (pole, pracovni, 0, stredni);
// seřaď pravou polovinu (bez středu)
mergeSort (pole, pracovni, stredni + 1, posledni);
// slij obě seřazené poloviny dohromady do neklesajícího pořadí
merge (pole, pracovni, prvni, stredni, posledni);
// zkopíruj pracovní pole do původního
for (int i = prvni; i <= posledni; i ++)
{
pole[i] = pracovni[i];
}
}
}
/**
* Metoda pro slévání dvou seřazených částí pole do pole pracovního.
* @param pole pole k seřazení (vstup)
* @param pracovni pracovní pole o stejné velikosti (výstup)
* @param prvni index prvního prvku
* @param stredni index středního prvku
* @param posledni index posledního prvku
* @author Vojtěch Hordějčuk
*/
private static <T extends Comparable<? super T>> void merge (final T pole[], final T pracovni[], int prvni, int stredni, int posledni)
{
// nastav index výsledného pole na začátek levé poloviny
int indexFinal = prvni;
// nastav levý index na začátek levé poloviny
int indexLevy = prvni;
// nastav pravý index na začátek pravé poloviny
// index je roven polovině plus jedna, protože střed patří do levé poloviny
int indexPravy = stredni + 1;
while (indexLevy <= stredni && indexPravy <= posledni)
{
if (pole[indexLevy].compareTo (pole[indexPravy]) <= 0)
{
// ulož menší prvek z levé poloviny a posuň ukazatele
pracovni[indexFinal ++] = pole[indexLevy ++];
}
else
{
// ulož menší prvek z pravé poloviny a posuň ukazatele
pracovni[indexFinal ++] = pole[indexPravy ++];
}
}
// zkopíruj zbývající prvky z levé části pole
while (indexLevy <= stredni)
{
pracovni[indexFinal ++] = pole[indexLevy ++];
}
// zkopíruj zbývající prvky z pravé části pole
while (indexPravy <= posledni)
{
pracovni[indexFinal ++] = pole[indexPravy ++];
}
}
* Implementace algoritmu merge sort.
* @param pole pole k seřazení
* @author Vojtěch Hordějčuk
*/
public static <T extends Comparable<? super T>> void mergeSort (final T pole[])
{
// vytvoř dočasné pole o stejné velikosti
T pracovni[] = new T[pole.length];
// spusť rekurzivní řazení pro celé pole
mergeSort (pole, pracovni, 0, pole.length - 1);
}
/**
* Hlavní metoda pro rekurzivní řazení.
* @param pole pole k seřazení
* @param pracovni pracovní pole o stejné velikosti
* @param prvni index prvního prvku
* @param posledni index poslední prvku
* @author Vojtěch Hordějčuk
*/
private static <T extends Comparable<? super T>> void mergeSort (final T pole[], final T pracovni[], int prvni, int posledni)
{
// řadit je nutné pouze část pole s více než jedním prvkem
if (prvni != posledni)
{
// vypočítej index středu
int stredni = (prvni + posledni) / 2;
// seřaď levou polovinu (včetně středu)
mergeSort (pole, pracovni, 0, stredni);
// seřaď pravou polovinu (bez středu)
mergeSort (pole, pracovni, stredni + 1, posledni);
// slij obě seřazené poloviny dohromady do neklesajícího pořadí
merge (pole, pracovni, prvni, stredni, posledni);
// zkopíruj pracovní pole do původního
for (int i = prvni; i <= posledni; i ++)
{
pole[i] = pracovni[i];
}
}
}
/**
* Metoda pro slévání dvou seřazených částí pole do pole pracovního.
* @param pole pole k seřazení (vstup)
* @param pracovni pracovní pole o stejné velikosti (výstup)
* @param prvni index prvního prvku
* @param stredni index středního prvku
* @param posledni index posledního prvku
* @author Vojtěch Hordějčuk
*/
private static <T extends Comparable<? super T>> void merge (final T pole[], final T pracovni[], int prvni, int stredni, int posledni)
{
// nastav index výsledného pole na začátek levé poloviny
int indexFinal = prvni;
// nastav levý index na začátek levé poloviny
int indexLevy = prvni;
// nastav pravý index na začátek pravé poloviny
// index je roven polovině plus jedna, protože střed patří do levé poloviny
int indexPravy = stredni + 1;
while (indexLevy <= stredni && indexPravy <= posledni)
{
if (pole[indexLevy].compareTo (pole[indexPravy]) <= 0)
{
// ulož menší prvek z levé poloviny a posuň ukazatele
pracovni[indexFinal ++] = pole[indexLevy ++];
}
else
{
// ulož menší prvek z pravé poloviny a posuň ukazatele
pracovni[indexFinal ++] = pole[indexPravy ++];
}
}
// zkopíruj zbývající prvky z levé části pole
while (indexLevy <= stredni)
{
pracovni[indexFinal ++] = pole[indexLevy ++];
}
// zkopíruj zbývající prvky z pravé části pole
while (indexPravy <= posledni)
{
pracovni[indexFinal ++] = pole[indexPravy ++];
}
}
zdrojový kód v jazyce Java - zobrazit (2878 znaků)
Implementace (Prolog)
%
ROZDĚLOVÁNÍ
% ===========
% triviální případy
divide([],[],[]).
divide([A],[A],[]).
% první prvek seznamu vložit na začátek druhého, druhý prvek na začátek třetího, poté rekurzivně zopakovat se zbytkem seznamu
divide([H1,H2|T],[H1|T1],[H2|T2]) :- divide(T,T1,T2).
% SLÉVÁNÍ
% =======
% triviální případy
merge(A,[],A).
merge([],A,A).
% je-li prvek H1 menší nebo roven H2, vložit jej na začátek seznamu a rekurzivně opakovat
merge([H1|T1],[H2|T2],[H1|Y]) :- H1 =< H2,merge(T1,[H2|T2],Y).
% je-li prvek H1 větší než H2, vložit jej na konec seznamu a rekurzivně opakovat
merge([H1|T1],[H2|T2],[H2|Y]) :- H1 > H2,merge([H1|T1],T2,Y).
% ŘAZENÍ
% ======
% triviální případy
sorter([],[]).
sorter([A],[A]).
% zajistit správné pořadí dvojice prvků
sorter([A,B],[A,B]) :- A =< B.
sorter([A,B],[B,A]) :- A > B.
% obecný případ - rozdělit pole na dvě části, seřadit je a poté spojit dohromady
sorter(A,B) :- divide(A,P1,P2),sorter(P1,R1),sorter(P2,R2),merge(R1,R2,B).
% ===========
% triviální případy
divide([],[],[]).
divide([A],[A],[]).
% první prvek seznamu vložit na začátek druhého, druhý prvek na začátek třetího, poté rekurzivně zopakovat se zbytkem seznamu
divide([H1,H2|T],[H1|T1],[H2|T2]) :- divide(T,T1,T2).
% SLÉVÁNÍ
% =======
% triviální případy
merge(A,[],A).
merge([],A,A).
% je-li prvek H1 menší nebo roven H2, vložit jej na začátek seznamu a rekurzivně opakovat
merge([H1|T1],[H2|T2],[H1|Y]) :- H1 =< H2,merge(T1,[H2|T2],Y).
% je-li prvek H1 větší než H2, vložit jej na konec seznamu a rekurzivně opakovat
merge([H1|T1],[H2|T2],[H2|Y]) :- H1 > H2,merge([H1|T1],T2,Y).
% ŘAZENÍ
% ======
% triviální případy
sorter([],[]).
sorter([A],[A]).
% zajistit správné pořadí dvojice prvků
sorter([A,B],[A,B]) :- A =< B.
sorter([A,B],[B,A]) :- A > B.
% obecný případ - rozdělit pole na dvě části, seřadit je a poté spojit dohromady
sorter(A,B) :- divide(A,P1,P2),sorter(P1,R1),sorter(P2,R2),merge(R1,R2,B).
zdrojový kód v jazyce PROLOG - zobrazit (993 znaků)
Ukázkový běh
| Pole | Operace |
|---|---|
| 5 3 2 1 4 2 | Toto pole chceme seřadit merge sortem. |
| 5 3 2 . 1 4 2 | Rozdělení na poloviny. |
| 5 3 . 2 .. 1 4 . 2 | Rozdělení na další poloviny. |
| 5 . 3 .. 2 … 1 . 4 .. 2 | Rozdělení na další poloviny (nyní máme jednoprvková pole). |
| 3 5 . 2 .. 1 4 . 2 | Slévání nejmenších (triviálních) polí do větších. |
| 2 3 5 . 1 2 4 | Slévání dalších polí do větších. |
| 1 2 2 3 4 5 | Slévání dalších polí do větších. |
| 1 2 2 3 4 5 | Pole je seřazeno. |
rekurzivní diagram algoritmu merge sort
Asymptotická složitost

Reference
Hledat na Wiki
Wiki - aktuálně
- 7. března 2010
Grupoid, pologrupa, monoid, grupa, změny - 7. března 2010
Grupoid, pologrupa, monoid, grupa, změny - 7. března 2010
Grupoid, pologrupa, monoid, grupa, změny - 3. března 2010
Komprese dat, změny - 3. března 2010
Komprese dat, změny - 3. března 2010
Seznam (datová struktura), změny - 27. února 2010
Jazyk C, změny - 27. února 2010
Jazyk C, změny - 27. února 2010
Jazyk C, změny - 27. února 2010
Jazyk C, změny