Skip to main content

Algorithm Walkthrough

Merge Sort operates in two distinct phases: dividing the array into smaller pieces, and merging those pieces back together in sorted order.
This implementation includes debug output that shows the sorting process in action at MergeSort.java:47-51

Phase 1: The Divide Phase

The algorithm recursively splits the array into halves until each subarray contains only one element.

Finding the Middle Point

The implementation calculates the midpoint carefully to avoid integer overflow:
int mitadArreglo = indiceInicio + (indiceFin - indiceInicio) / 2;
This formula is preferred over (indiceInicio + indiceFin) / 2 because it prevents potential integer overflow when dealing with large indices.

Recursive Division

From MergeSort.java:23-44, the recursive method handles the division:
private static void mergeSortReordenamiento(int[] originalArreglo, int[] depositoArreglo, 
        int indiceInicio, int indiceFin) {
    
    if (indiceInicio >= indiceFin) {
        return;  // Base case: single element is already sorted
    }
    
    int mitadArreglo = indiceInicio + (indiceFin - indiceInicio) / 2;
    
    // Recursively sort left half
    mergeSortReordenamiento(originalArreglo, depositoArreglo, indiceInicio, mitadArreglo);
    
    // Recursively sort right half
    mergeSortReordenamiento(originalArreglo, depositoArreglo, mitadArreglo + 1, indiceFin);
    
    // Merge the sorted halves
    merge(originalArreglo, depositoArreglo, indiceInicio, mitadArreglo, indiceFin);
}
1

Base Case Check

If indiceInicio >= indiceFin, the subarray has 0 or 1 elements and is already sorted
2

Calculate Midpoint

Find the middle index to split the array into two halves
3

Sort Left Half

Recursively call the method on the left portion [indiceInicio, mitadArreglo]
4

Sort Right Half

Recursively call the method on the right portion [mitadArreglo + 1, indiceFin]
5

Merge Results

Combine the two sorted halves using the merge function

Phase 2: The Merge Phase

After dividing, the algorithm merges sorted subarrays back together. This is where the actual sorting happens.

Visual Example

Let’s trace through the example array from MergeSort.java:108:
Original: [10, 5, 8, 9, 2, 3, 1, 6]

Divide:
[10, 5, 8, 9]  [2, 3, 1, 6]
[10, 5]  [8, 9]  [2, 3]  [1, 6]
[10] [5] [8] [9] [2] [3] [1] [6]

Merge:
[5, 10]  [8, 9]  [2, 3]  [1, 6]
[5, 8, 9, 10]  [1, 2, 3, 6]
[1, 2, 3, 5, 6, 8, 9, 10]

The Merge Process

The merge() method from MergeSort.java:54-103 combines two sorted subarrays:
private static void merge(int[] originalArreglo, int[] depositoArreglo, 
        int indiceInicio, int mitadArreglo, int indiceFin) {
    
    // Copy current section to temporary array
    for (int i = indiceInicio; i <= indiceFin; i++) {
        depositoArreglo[i] = originalArreglo[i];
    }
    
    int contadorPrimerArreglo = indiceInicio;      // Left half pointer
    int contadorSegundoArreglo = mitadArreglo + 1;  // Right half pointer
    int acompladorArreglos = indiceInicio;          // Output pointer
    
    // Compare and merge
    while (contadorPrimerArreglo <= mitadArreglo && 
           contadorSegundoArreglo <= indiceFin) {
        
        if (depositoArreglo[contadorPrimerArreglo] <= 
            depositoArreglo[contadorSegundoArreglo]) {
            originalArreglo[acompladorArreglos] = depositoArreglo[contadorPrimerArreglo];
            contadorPrimerArreglo++;
        } else {
            originalArreglo[acompladorArreglos] = depositoArreglo[contadorSegundoArreglo];
            contadorSegundoArreglo++;
        }
        acompladorArreglos++;
    }
    
    // Copy remaining elements from left half (if any)
    while (contadorPrimerArreglo <= mitadArreglo) {
        originalArreglo[acompladorArreglos] = depositoArreglo[contadorPrimerArreglo];
        contadorPrimerArreglo++;
        acompladorArreglos++;
    }
}
1

Copy to Temporary Array

Copy the current section from the original array to the temporary array (depositoArreglo)
2

Initialize Pointers

  • contadorPrimerArreglo: points to the start of the left half
  • contadorSegundoArreglo: points to the start of the right half
  • acompladorArreglos: points to where we write in the original array
3

Compare and Merge

Compare elements from both halves and place the smaller one into the original array
4

Copy Remaining Elements

If the left half has remaining elements, copy them (right half elements are already in place)

Detailed Merge Example

Let’s merge two sorted subarrays: [5, 10] and [8, 9]
Step 1: Copy to temporary array
depositoArreglo: [5, 10, 8, 9]

Step 2: Initialize pointers
Left pointer (contadorPrimerArreglo) → 5
Right pointer (contadorSegundoArreglo) → 8
Output pointer (acompladorArreglos) → position 0

Step 3: Compare and merge
Compare 5 vs 8 → 5 is smaller
  originalArreglo[0] = 5
  Move left pointer → 10
  Move output pointer → position 1

Compare 10 vs 8 → 8 is smaller
  originalArreglo[1] = 8
  Move right pointer → 9
  Move output pointer → position 2

Compare 10 vs 9 → 9 is smaller
  originalArreglo[2] = 9
  Move right pointer → (exhausted)
  Move output pointer → position 3

Step 4: Copy remaining
Left half has 10 remaining
  originalArreglo[3] = 10

Result: [5, 8, 9, 10]
The algorithm only needs to check for remaining elements in the left half. Elements in the right half are already in their correct positions in the original array.

Entry Point and Validation

The public mergeSort() method at MergeSort.java:6-21 serves as the entry point:
public static void mergeSort(int[] arreglo) {
    if (arreglo == null || arreglo.length <= 1) {
        return;  // Already sorted or invalid
    }
    
    int[] depositoArreglo = new int[arreglo.length];
    mergeSortReordenamiento(arreglo, depositoArreglo, 0, arreglo.length - 1);
}
1

Input Validation

Check if the array is null or has 0-1 elements (already sorted)
2

Create Temporary Array

Allocate auxiliary space equal to the input array size
3

Initiate Sorting

Call the recursive method with the full array range

Recursion Tree Visualization

For the array [10, 5, 8, 9, 2, 3, 1, 6], the recursion forms this tree:
                [10,5,8,9,2,3,1,6]
                       /  \
            [10,5,8,9]      [2,3,1,6]
              /    \          /    \
          [10,5]  [8,9]    [2,3]  [1,6]
           / \     / \      / \    / \
        [10] [5] [8] [9] [2] [3] [1] [6]
           \ /     \ /      \ /    \ /
          [5,10]  [8,9]    [2,3]  [1,6]
              \    /          \    /
            [5,8,9,10]      [1,2,3,6]
                       \  /
                [1,2,3,5,6,8,9,10]
The tree height is log₂(n), which is why the time complexity involves a logarithmic factor.

Key Implementation Details

Stability

The algorithm is stable because of this comparison at MergeSort.java:76:
if (depositoArreglo[contadorPrimerArreglo] <= depositoArreglo[contadorSegundoArreglo])
The <= operator ensures that when elements are equal, the one from the left subarray (which appeared first originally) is placed first.

In-Place vs Out-of-Place

This implementation is out-of-place because it uses a temporary array:
int[] depositoArreglo = new int[arreglo.length];
This requires O(n) additional space but simplifies the merging logic.
The temporary array is allocated once at the start and reused throughout all recursive calls, which is more efficient than allocating new arrays at each level.

Try It Yourself

The main method at MergeSort.java:105-139 provides a working example:
int[] originalArreglo = {10, 5, 8, 9, 2, 3, 1, 6};
mergeSort(originalArreglo);

for (int num : originalArreglo) {
    System.out.print(num + " ");
}
Run this to see the algorithm in action, including the debug output showing each merge step!
Uncomment the code at lines 117-138 to test with randomly generated arrays of varying sizes.

Next Steps

Complexity Analysis

Learn about the time and space complexity of this implementation

Build docs developers (and LLMs) love