Bc. Vojtěch Hordějčuk

„Čas je způsob, jakým příroda zajišťuje, aby se vše neodehrálo najednou.” - J. A. Wheeler

Domů » Wiki » Řadící algoritmy » Merge sort

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 input pole k seřazení
 * @author Vojtěch Hordějčuk
 */

public static <T extends Comparable<? super T>> void mergeSort(final T input[])
{
  // vytvoř dočasné pole o stejné velikosti
  final Object temp[] = new Object[input.length];

  // spusť rekurzivní řazení pro celé pole
  mergeSort(input, temp, 0, input.length - 1);
}

/**
 * Hlavní rekurzivní funkce.
 *
 * @param input pole k seřazení
 * @param temp dočasné pracovní pole
 * @param iLeft index levého prvku
 * @param iRight index pravého prvku
 */

private static <T extends Comparable<? super T>> void mergeSort(final T[] input, final Object[] temp, final int iLeft, final int iRight)
{
  // jestliže je koncový index větší než počáteční, je nutné řadit

  if (iLeft < iRight)
  {
    // vypočítej index středu
    final int iCenter = (iLeft + iRight) / 2;

    // spusť řazení rekurzivně na levou polovinu (včetně středu)
    mergeSort(input, temp, iLeft, iCenter);

    // spusť řazení rekurzivně na pravou polovinu (bez středu)
    mergeSort(input, temp, iCenter + 1, iRight);

    // zkombinuj (slij) obě části
    merge(input, temp, iLeft, iCenter + 1, iRight);
  }
}

/**
 * Interní slévací metoda.
 *
 * @param input pole k seřazení
 * @param temp dočasné pracovní pole
 * @param iLeft index levého prvku
 * @param iRight index prvního prvku pravé části
 * @param iEnd index posledního prvku pravé části
 */

@SuppressWarnings("unchecked")
private static <T extends Comparable<? super T>> void merge(final T[] input, final Object[] temp, int iLeft, int iRight, int iEnd)
{
  // poslední index levé části
  final int iLeftEnd = iRight - 1;

  // aktuální pozice
  int iCurrent = iLeft;

  // počet prvků
  final int numElements = iEnd - iLeft + 1;

  while ((iLeft <= iLeftEnd) && (iRight <= iEnd))
  {
    if (input[iLeft].compareTo(input[iRight]) <= 0)
    {
      temp[iCurrent++] = input[iLeft++];
    }
    else
    {
      temp[iCurrent++] = input[iRight++];
    }
  }

  // zkopíruj zbytek levé poloviny
  while (iLeft <= iLeftEnd)
  {
    temp[iCurrent++] = input[iLeft++];
  }

  // zkopíruj zbytek pravé poloviny
  while (iRight <= iEnd)
  {
    temp[iCurrent++] = input[iRight++];
  }

  // zkopíruj dočasné pole zpátky
  for (int i = 0; i < numElements; i++)
  {
    input[iEnd] = (T) temp[iEnd];
    iEnd--;
  }
}

zdrojový kód (Java) - zobrazit (2344 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).

zdrojový kód (PROLOG) - zobrazit (963 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

O(n \cdot \log n)

Reference