Merge sort – triedenie delením a spájaním v Java

Triediace algoritmy sa používajú na vzostupné alebo zostupné preusporiadanie prvkov poľa alebo zoznamu podľa numerického alebo lexikografického poradia. Triedenie je veľmi dôležité pre optimálny výkon iných algoritmov, ktoré vyžadujú triedené vstupné dáta.

Existuje množstvo rozličných triediacich algoritmov. Výber vhodného algoritmu závisí na faktoroch ako veľkosť a vlastnosti vstupných dát, dostupná pamäť a požiadavky na časovú a priestorovú náročnosť.

Aby sme ti uľahčili výber, predstavíme si v našom seriáli postupne najznámejšie algoritmy triedenia dát, vysvetlíme si ich princípy, výhody a nevýhody a naprogramujeme si ich v Jave.

Doteraz sme sa venovali nasledujúcim triediacim algoritmom:

Dnes sa budeme venovať triedeniu pomocou spájania – mergovania (angl. Merge sort).

Merge sort

Merge sort je veľmi efektívny stabilný algoritmus triedenia, ktorý bol vynájdený v roku 1945 Johnom von Neumannom, jedným z priekopníkov v oblasti informatiky. Využíva techniku „rozdeľ a panuj“ (divide and conquer), základná myšlienka spočíva v tom, že ak máme komplexný problém snažíme sa ho rozložiť na menšie časti a tie vyriešime samostatne. Výsledné riešenie je teda výsledkom riešení jednotlivých menších podproblémov. Takéto rozloženie problémov sa potom dá výborne paralelizovať a efektívne riešiť na moderných viacjadrových procesoroch.

Vstupný vektor dát rozdelíme na niekoľko približne rovnakých častí. Najčastejšia verzia používa delenie na 2 časti, ide o binárny merge, v prípade viac častí ide o tzv. K-way merge. Poďme sa pozrieť, ako sa dá táto technika využiť pri triedení dát.

Merge sort algoritmus – princíp fungovania

Vstupné dáta sa rozdelia na približne dve rovnaké časti, ak je počet elementov nepárny, prvá časť má o 1 prvok viac. Každá s týchto častí poľa sa ďalej delí na polovicu a to sa opakuje až pokiaľ časť neobsahuje iba jeden prvok. Využíva sa pri tom rekurzívne volanie. Pole s jedným prvkom sa považuje sa zotriedené. Akonáhle sa dané časti už nedajú rozdeliť, tak sa vezmú obe posledné polovice a ich prvky sa spoja tak, aby aj výsledný zoznam zostal zotriedený. Toto sa opakuje, až dostane pôvodný vektor dát v zotriedenej podobe. Najlepšie to pochopíme na príklade na nasledujúcom obrázku.

Vizuálne znázornenie algoritmu Merge-Sort. Červená časť je fáza delenia a zelená je fáza mergovania (spájania výsledkov). Zdroj wikipedia
Vizuálne znázornenie algoritmu Merge-Sort. Červená časť je fáza delenia a zelená je fáza mergovania (spájania výsledkov). Zdroj wikipedia

Merge sort animácie, vizualizácia, gif

Animácia merge sort vyzerá nasledovne:

CobaltBlue, CC BY-SA 4.0, via Wikimedia Commons
CobaltBlue, CC BY-SA 4.0, via Wikimedia Commons

Zdroj: commons.wikimedia.org/wiki/Category:Animations_of_sort_algorithms

Animáciu algoritmu nájdeš napríklad na stránke Toptal. Nachádza sa tam 8 typov algoritmov, animáciu si môžeš pustiť aj pozadu.

Na tejto stránke si môžeš prejsť jednotlivými krokmi algoritmu, vďaka čomu si vieš overiť časovú náročnosť algoritmu.

Výhody algoritmu Merge sort

  • Stabilný algoritmus
  • Efektívny aj obrovské sady dát
  • Dá sa ľahko paralelizovať a rozložiť tak výpočtovú záťaž na viac procesorov

Nevýhody algoritmu Merge sort

  • Na uloženie dát pri mergovaní podpolí sa vyžaduje dodatočný pamäťový priestor

Merge sort časová náročnosť

Algoritmus Metóda Časová náročnosť Pamäť Stabilný
najhoršia priemer najlepšia
Merge sort mergovanie O(n log n) O(n log n) O(n log n) O(n) áno

Merge Sort implementácia, Java kód

Teraz si ukážeme implementáciu algoritmu Merge sort v Jave.

MergeSort.java

package sorting;

public class MergeSort {
    public void sort(int data[], int left, int right)
    {
        if (left < right) {
            // Find the cutting point
            int middle = (left + right) / 2;
            // Sort first and second part separately
            sort(data, left, middle);
            sort(data, middle + 1, right);
            // Merge both sorted halves
            merge(data, left, middle, right);
        }
    }

    public void merge(int[] data, int left, int middle, int right) {
        // Calculate sizes of left and right merge arrays
        int leftPartSize = middle - left + 1;
        int rightPartSize = right - middle;
        // Create them
        int[] leftPart = new int[leftPartSize];
        int[] rightPart = new int[rightPartSize];
        // Fill them
        for (int i = 0; i < leftPartSize; i++) {
            leftPart[i] = data[left + i];
        }
        for (int j = 0; j < rightPartSize; j++) {
            rightPart[j] = data[middle + 1 + j];
        }

        System.out.print("L: ");
        printArray(leftPart);
        System.out.print("+ R: ");
        printArray(rightPart);
        System.out.print("=> ");

        // k is index of merged array
        int i = 0, j = 0, k = left;
        while (i < leftPartSize && j < rightPartSize) {
            if (leftPart[i] <= rightPart[j]) {
                data[k++] = leftPart[i++];
            }
            else {
                data[k++] = rightPart[j++];
            }
        }
        // Copy any remaining elements
        while (i < leftPartSize) {
            data[k++] = leftPart[i++];
        }
        while (j < rightPartSize) {
            data[k++] = rightPart[j++];
        }

        for (i = left; i < k; i++)
            System.out.print(data[i] + " ");
        System.out.println();
    }

    // Function to print an array
    public void printArray(int data[])
    {
        for (int i = 0; i < data.length; i++)
            System.out.print(data[i] + " ");
        //System.out.println();
    }
}

Main.java

import sorting.MergeSort;

public class Main {
    public static void main(String[] args) {
        int dataToSort[] = { 6, 5, 4, 2, 3, 1, 7 };
        MergeSort mergeSort = new MergeSort();
        System.out.print("Input: ");
        mergeSort.printArray(dataToSort);
        System.out.println("");
        mergeSort.sort(dataToSort, 0, dataToSort.length - 1);
        System.out.print("Sorted: ");
        mergeSort.printArray(dataToSort);
    }
}

Výstup z tohto príkladu je:

Merge sort výstup z príkladu

Pripravili sme pre teba súbory so spomínaným príkladom vo forme kódu, ktorý si môžeš spustiť priamo v Jave. Stiahni si kód Java MergeSort tu.

Ak si Java programátor a hľadáš prácu, pozri si naše benefity pre zamestnancov a reaguj na najnovšie ponuky práce.

 

O autorovi

Jozef Wagner

Java Developer Senior

Viac ako 10 rokov programujem v Jave, momentálne pracujem v msg life Slovakia ako Java programátor senior a pomáham zákazníkom implementovať ich požiadavky do poistného softvéru Life Factory. Vo voľnom čase si rád oddýchnem v lese, prípadne si zahrám nejakú dobrú počítačovú hru.

Daj nám o sebe vedieť