Il merge sort è un algoritmo di ordinamento molto intuitivo e abbastanza rapido, che utilizza un processo di risoluzione ricorsivo.

L'idea alla base del merge sort è il procedimento Divide et Impera, che consiste nella suddivisione del problema in sottoproblemi via via più piccoli.

Il merge sort opera quindi dividendo l'insieme da ordinare in due metà e procedendo all'ordinamento delle medesime ricorsivamente. Quando si sono divise tutte le metà si procede alla loro fusione (merge appunto) costruendo un insieme ordinato.

L'algoritmo fu inventato da John von Neumann nel 1945.

Fase 1: Impera

Supponendo di avere due sequenze già ordinate. Per unirle, l'algoritmo mergesort estrae ripetutamente il minimo delle due sequenze in ingresso e lo pone in una sequenza in uscita.

Dati un array   e due indici x ≤ y, denotiamo   la porzione dell'array A costituita dagli elementi  .


Esempio pratico

Supponiamo di dover ordinare il seguente array:

10 3 15 2 1 4 9 0

Si procede dividendolo in metà successive, fino ad arrivare a coppie:

10 3

15 2

 1 4

 9 0

A questo punto si fondono (merge) in maniera ordinata gli elementi, riunendo le metà:

10 3 -> 3 10

15 2 -> 2 15

 1 4 -> 1  4

 9 0 -> 0  9

Al passo successivo:

3 10 2 15 -> 2 3 10 15

1  4 0  9 -> 0 1  4  9

Infine:

2 3 10 15 0 1 4 9 -> 0 1 2 3 4 9 10 15

L'esecuzione ricorsiva all'interno del calcolatore non avviene nell'ordine descritto sopra, ma si è preferito formulare l'esempio in questo modo in maniera da renderlo più comprensibile.

Pseudocodice

Segue lo pseudocodice per l'algoritmo.

 merge (a[], left, center, right)  
    i ← left
    j ← center + 1
    k ← 0
 
    while ((i <= center) && (j <= right)) do
       if (a[i] <= a[j]) then
          b[k] ← a[i]
          i ← i + 1
 	else
 	   b[k] = a[j]
 	   j ← j + 1  
       k ← k + 1
    end while
 
    while (i <= center) do
 	  b[k] ← a[i]
 	  j ← j + 1
 	  k ← k + 1
    end while
 
    while (j <= right) do
 	  b[k] ← a[j] 
 	  j ← j + 1
         k ← k + 1
    end while
 
    for k ← left to right do
 	a[k] ← b[k - left]
 
 mergesort (a[], left, right)
    if (left < right) then
 	center ← (left + right) / 2
 	mergesort(a, left, center)
 	mergesort(a, center+1, right)
 	merge(a, left, center, right)

Implementazioni

Seguono alcune implementazioni in vari linguaggi.

 #include <stdio.h>
 #define LEN 8
 
 void merge(int a[], int left, int center, int right)
 {
 	int i, j, k; 
 	int b[LEN];
 
 	i = left;
 	j = center+1;
 	k = 0;
 
 	while ((i<=center) && (j<=right)) {
 		if (a[i] <= a[j]) {
 			b[k] = a[i];
 			i++;
 		}
 		else {
 			b[k] = a[j];
 			j++;
 		}
 
 		k++;
 	}
 
 	while (i<=center) {
 		b[k] = a[i]; 
 		i++;
 		k++;
 	}
 
 	while (j<=right) {
 		b[k] = a[j]; 
 		j++; 
 		k++;
 	}
 
 	for (k=left; k<=right; k++)
 		a[k] = b[k-left];
 }
 
 void mergesort(int a[], int left, int right)
 {
 	int center;
 
 	if(left<right){
 		center = (left+right)/2;
 		mergesort(a, left, center);
 		mergesort(a, center+1, right);
 		merge(a, left, center, right);
 	}
 }
 
 int main(void)
 {
 	int a[LEN], i;
      
 	for(i=0; i<LEN; i++) {
 		printf(": ");
 		scanf("%d", &a[i]);  
 	}
 
 	mergesort(a, 0, LEN-1);
  
 	printf("[ ");
 
 	for(i=0; i<LEN; i++)
 		printf("%d ", a[i]);  
 
 	printf("]\n");
 
 	return 0;
 }


 void merge(int a[], int start, int center, int end, int size)
{
       int i, j, k; 
       int app[size];

       i = start;
       j = center+1;
       k = 0;

       while ((i<=center) && (j<=end)) {
               if (a[i] <= a[j]) {
                       app[k++] = a[i++];
               }
               else {
                       app[k++] = a[j++];
               }
       }

       while (i<=center) 
               app[k++] = a[i++]; 

       while (j<=end) 
               app[k++] = a[j++]; 

       for (k=start; k<=end; k++)
               a[k] = app[k-start];
               printv(a,size);
               //printf("merging.. {v[%d] - v[%d]} whit {v[%d] - v[%d]} \n",start,center,center+1,end);
}

void iterativemergesort(int a[],int size)
{
     
     int sizetomerge=size-1;
     size--;
     int i;
     int n=2;
     while (n<sizetomerge*2)
     {
     
     for (i=0; (i+n-1)<=sizetomerge; i+=n)
     {
         merge(a,i,(i+i+n-1)/2,i+(n-1),sizetomerge);
     }
    i--;
  if ((sizetomerge+1)%n!=0){ if (size>sizetomerge) {merge (a,sizetomerge -((sizetomerge)%n),sizetomerge,size,size);}
                         sizetomerge=sizetomerge-((sizetomerge+1)%n);}
     n=n*2;
     }
     
     if (size>sizetomerge) {  
                        merge (a,0,size-(size-sizetomerge),size,size);}
}

dal main chiamare iterativemergesort(vettore,dimensione del vettore);

(il metodo iterativo è più preformante ma è di più difficile comprensione logica)

typedef int Item;


int main() {
	const int n=8;
	int a[n] = {10, 3, 15, 2, 1, 4, 9, 0};
	mergesort(a,0,n-1);
	return 0;
}

 void mergesort(Item a[], int left, int right) {
	if (left<right) {
		int center = (left+right)/2;
		mergesort(a, left, center);
		mergesort(a, center+1, right);
		merge(a, left, center, right);
	}
}

void merge(Item a[], int left, int center, int right) {
	const int n=8;
	static Item aux[n];
	int i,j;
	for (i = center+1; i > left; i--) aux[i-1] = a[i-1];
	for (j = center; j < right; j++) aux[right+center-j] = a[j+1];
	for (int k = left; k <= right; k++)
	if (aux[j] < aux[i]) a[k] = aux[j--];
		else a[k] = aux[i++];
}
import java.util.*;
   
public class Sort{
       private static void mergeSort(int []a, int []vectorTemp, int left, int right ){
               if (left < right){
                       int center = (left + right) /2;
                       mergeSort (a, vectorTemp, left, center);
                       mergeSort (a, vectorTemp, center+1, right);
                       merge (a, vectorTemp, left, center+1, right);
               }
       }
       
       public static void mergeSort(int[]a ){
		int vectorTemp [];
		vectorTemp =new int [a.length];
		mergeSort (a, vectorTemp, 0, a.length-1);
       }

       private static void merge (int[]a, int[]vectorAux, int posLeft, int posRight, int posEnd ){
		int endLeft = posRight -1;
		int posAux = posLeft;
		int numElemen = posEnd - posLeft +1;

		while (posLeft <= endLeft && posRight <=posEnd)
		if ((a[ posLeft ])< (a[posRight]))        
			vectorAux [posAux++]=a[posLeft++];
		else
			vectorAux [posAux++] = a[posRight++];

		while (posLeft <= endLeft)
			vectorAux [posAux++]=a[posLeft++];

		while (posRight <= posEnd)
			vectorAux [posAux++]=a[posRight++];

		for (int i=0;i<numElemen;i++,posEnd--)
			a[posEnd]=vectorAux[posEnd];
       }
       

       public static void print(int[] vector) {
               System.out.print("{");
               for(int i=0;i<vector.length;i++) {
                       System.out.print(" "+vector[i]);
                       if(i==vector.length-1) System.out.println(" }");
                       else System.out.print(",");
               }
       }
       
       public static void main(String[] args) {
               int vector[]= { 10, 3, 15, 2, 1, 4, 9, 0};
               System.out.println("Array Non Ordinato:");
               print(vector);
               System.out.println();
               mergeSort(vector);
               System.out.println("Array Ordinato Con MergeSort:");
               print(vector);
       }
}

Analisi delle prestazioni

Il tempo di esecuzione dell'algoritmo Merge Sort è Θ(n log n). Infatti:

- la funzione merge ha costo Θ(n), e mergesort richiama se stessa due volte ogni volta su metà della porzione di input. Quindi possiamo associare al tempo di esecuzione di mergesort la funzione temporale

 

che per il secondo caso del teorema master è Θ(nlogn).