Merge Sort

Problem and examples

Given an array of integers, your task is to sort the array in ascending order using the Merge Sort algorithm.

Merge Sort is a divide and conquer algorithm. It divides the array into halves, recursively sorts them, and then merges the sorted halves into one final sorted array.

This algorithm is stable and guarantees a worst-case time complexity of O(n log n).

Input Array Sorted Output Description
[4, 2, 7, 1, 9, 3] [1, 2, 3, 4, 7, 9] Unsorted array is recursively divided and merged into a sorted version.
[1, 2, 3, 4] [1, 2, 3, 4] Already sorted input still goes through divide and merge steps.
[4, 4, 4, 4] [4, 4, 4, 4] All elements are equal. Merge Sort maintains the original order (stable).
[10] [10] Single-element array is already sorted. No merging needed.
[] [] Empty array returns an empty result. No action is performed.
[5, -2, 0, 3, -7] [-7, -2, 0, 3, 5] Handles negative numbers and zero correctly in sorting.

Explore Merge Sort

Load an example or enter your own input. Use the player controls to follow each change.

Understanding the Problem

Imagine you have a messy list of numbers — like cards scattered on a table — and your goal is to sort them in increasing order. The problem we’re solving is: How can we sort an array efficiently?

One powerful way to do this is by using an algorithm called Merge Sort. It doesn't sort the whole thing in one go. Instead, it breaks the array into smaller parts, sorts those, and then puts them back together — all in the right order!

Our Strategy: Divide and Conquer

Merge Sort uses a classic strategy called Divide and Conquer. That means:

  1. First, divide the array into halves.
  2. Then, recursively sort each half.
  3. Finally, merge the sorted halves into a single sorted array.

Step-by-Step Example

Let’s go through a clear example to see how Merge Sort works in action.

Input: [4, 2, 7, 1]

Step 1: Divide the array into two halves → [4, 2] and [7, 1]

Step 2: Break those down further → [4], [2], [7], [1]

Now we have individual elements, and by definition, each single-element array is already sorted.

Step 3: Merge them back in sorted order:

  • [4] and [2] → merge into [2, 4]
  • [7] and [1] → merge into [1, 7]

Step 4: Merge [2, 4] and [1, 7] → final result: [1, 2, 4, 7]

Exploring Different Edge Cases

Case 1: Already Sorted Array

[1, 2, 3, 4] is already sorted. Merge Sort will still divide and merge, but it won’t need to do much actual rearranging.

Case 2: All Elements Are the Same

Input: [5, 5, 5, 5] → Output: [5, 5, 5, 5]

Merge Sort keeps the original order, which is known as stability.

Case 3: Single Element

Input: [10] → Already sorted, nothing to do.

Case 4: Empty Array

Input: [] → No elements to sort, so the output is also [].

Case 5: Negative Numbers

Merge Sort handles all numbers just fine, even if they’re negative.

Example: [5, -2, 0] → [-2, 0, 5]

Why Merge Sort Is a Great Choice

Merge Sort always takes O(n log n) time — even in the worst case — which is very efficient for large datasets. Plus, because it's a stable sort, it's perfect when the relative order of equal elements matters (like sorting records by multiple criteria).

  1. Divide the unsorted array into n subarrays, each containing one element.
  2. Repeatedly merge subarrays to produce new sorted subarrays until there is only one subarray remaining.
  3. The final subarray is the sorted array.

Implementation and efficiency

C implementation

#include <stdio.h>
#include <stdlib.h>

void merge(int arr[], int l, int m, int r) {
    int n1 = m - l + 1;
    int n2 = r - m;
    int *L = (int*) malloc(n1 * sizeof(int));
    int *R = (int*) malloc(n2 * sizeof(int));
    for (int i = 0; i < n1; i++)
        L[i] = arr[l + i];
    for (int j = 0; j < n2; j++)
        R[j] = arr[m + 1 + j];
    int i = 0, j = 0, k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1)
        arr[k++] = L[i++];
    while (j < n2)
        arr[k++] = R[j++];
    free(L);
    free(R);
}

void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}

int main() {
    int arr[] = {6, 3, 8, 2, 7, 4};
    int n = sizeof(arr) / sizeof(arr[0]);
    mergeSort(arr, 0, n - 1);
    printf("Sorted array is: ");
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

C++ implementation

#include <iostream>
#include <vector>
using namespace std;

void merge(vector<int>& arr, int l, int m, int r) {
    int n1 = m - l + 1;
    int n2 = r - m;
    vector<int> L(n1), R(n2);
    for (int i = 0; i < n1; i++) {
        L[i] = arr[l + i];
    }
    for (int j = 0; j < n2; j++) {
        R[j] = arr[m + 1 + j];
    }
    int i = 0, j = 0, k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }
    while (i < n1) {
        arr[k++] = L[i++];
    }
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

void mergeSort(vector<int>& arr, int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}

int main() {
    vector<int> arr = {6, 3, 8, 2, 7, 4};
    mergeSort(arr, 0, arr.size() - 1);
    cout << "Sorted array is: ";
    for (int num : arr) {
        cout << num << " ";
    }
    return 0;
}

Python implementation

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

if __name__ == '__main__':
    arr = [6, 3, 8, 2, 7, 4]
    sorted_arr = merge_sort(arr)
    print("Sorted array is:", sorted_arr)

Java implementation

public class MergeSort {
    public static void mergeSort(int[] arr, int l, int r) {
        if (l < r) {
            int m = l + (r - l) / 2;
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
            merge(arr, l, m, r);
        }
    }

    public static void merge(int[] arr, int l, int m, int r) {
        int n1 = m - l + 1;
        int n2 = r - m;
        int[] L = new int[n1];
        int[] R = new int[n2];
        for (int i = 0; i < n1; i++) {
            L[i] = arr[l + i];
        }
        for (int j = 0; j < n2; j++) {
            R[j] = arr[m + 1 + j];
        }
        int i = 0, j = 0, k = l;
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                arr[k++] = L[i++];
            } else {
                arr[k++] = R[j++];
            }
        }
        while (i < n1) {
            arr[k++] = L[i++];
        }
        while (j < n2) {
            arr[k++] = R[j++];
        }
    }

    public static void main(String[] args) {
        int[] arr = {6, 3, 8, 2, 7, 4};
        mergeSort(arr, 0, arr.length - 1);
        System.out.print("Sorted array is: ");
        for (int num : arr) {
            System.out.print(num + " ");
        }
    }
}

JavaScript implementation

function mergeSort(arr) {
    if (arr.length <= 1) {
        return arr;
    }
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    return merge(left, right);
}

function merge(left, right) {
    let result = [];
    while (left.length && right.length) {
        if (left[0] <= right[0]) {
            result.push(left.shift());
        } else {
            result.push(right.shift());
        }
    }
    return result.concat(left, right);
}

let arr = [6, 3, 8, 2, 7, 4];
arr = mergeSort(arr);
console.log("Sorted array is:", arr);

Go implementation

package main
import "fmt"

func merge(left, right []int) []int {
    var result []int
    i, j := 0, 0
    for i < len(left) && j < len(right) {
        if left[i] <= right[j] {
            result = append(result, left[i])
            i++
        } else {
            result = append(result, right[j])
            j++
        }
    }
    result = append(result, left[i:]...)
    result = append(result, right[j:]...)
    return result
}

func mergeSort(arr []int) []int {
    if len(arr) <= 1 {
        return arr
    }
    mid := len(arr) / 2
    left := mergeSort(arr[:mid])
    right := mergeSort(arr[mid:])
    return merge(left, right)
}

func main() {
    arr := []int{6, 3, 8, 2, 7, 4}
    sorted := mergeSort(arr)
    fmt.Println("Sorted array is:", sorted)
}

Rust implementation

fn merge_sort(mut arr: Vec<i32>) -> Vec<i32> {
    if arr.len() <= 1 {
        return arr;
    }
    let mid = arr.len() / 2;
    let left = merge_sort(arr[..mid].to_vec());
    let right = merge_sort(arr[mid..].to_vec());
    merge(left, right)
}

fn merge(left: Vec<i32>, right: Vec<i32>) -> Vec<i32> {
    let mut result = Vec::new();
    let mut i = 0;
    let mut j = 0;
    while i < left.len() && j < right.len() {
        if left[i] <= right[j] {
            result.push(left[i]);
            i += 1;
        } else {
            result.push(right[j]);
            j += 1;
        }
    }
    result.extend_from_slice(&left[i..]);
    result.extend_from_slice(&right[j..]);
    result
}

fn main() {
    let arr = vec![6, 3, 8, 2, 7, 4];
    let sorted = merge_sort(arr);
    println!("Sorted array is: {:?}", sorted);
}

Kotlin implementation

fun mergeSort(arr: IntArray): IntArray {
    if (arr.size <= 1) return arr
    val mid = arr.size / 2
    val left = mergeSort(arr.sliceArray(0 until mid))
    val right = mergeSort(arr.sliceArray(mid until arr.size))
    return merge(left, right)
}

fun merge(left: IntArray, right: IntArray): IntArray {
    var i = 0
    var j = 0
    val merged = mutableListOf<Int>()
    while (i < left.size && j < right.size) {
        if (left[i] <= right[j]) {
            merged.add(left[i])
            i++
        } else {
            merged.add(right[j])
            j++
        }
    }
    while (i < left.size) {
        merged.add(left[i])
        i++
    }
    while (j < right.size) {
        merged.add(right[j])
        j++
    }
    return merged.toIntArray()
}

fun main() {
    val arr = intArrayOf(6, 3, 8, 2, 7, 4)
    val sorted = mergeSort(arr)
    println("Sorted array is: " + sorted.joinToString(" "))
}

Swift implementation

func mergeSort(_ arr: [Int]) -> [Int] {
    guard arr.count > 1 else { return arr }
    let mid = arr.count / 2
    let left = mergeSort(Array(arr[0..<mid]))
    let right = mergeSort(Array(arr[mid..<arr.count]))
    return merge(left, right)
}

func merge(_ left: [Int], _ right: [Int]) -> [Int] {
    var merged: [Int] = []
    var i = 0, j = 0
    while i < left.count && j < right.count {
        if left[i] <= right[j] {
            merged.append(left[i])
            i += 1
        } else {
            merged.append(right[j])
            j += 1
        }
    }
    while i < left.count {
        merged.append(left[i])
        i += 1
    }
    while j < right.count {
        merged.append(right[j])
        j += 1
    }
    return merged
}

var arr = [6, 3, 8, 2, 7, 4]
arr = mergeSort(arr)
print("Sorted array is:", arr)

TypeScript implementation

function mergeSort(arr: number[]): number[] {
    if (arr.length <= 1) {
        return arr;
    }
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    return merge(left, right);
}

function merge(left: number[], right: number[]): number[] {
    let result: number[] = [];
    let i = 0, j = 0;
    while (i < left.length && j < right.length) {
        if (left[i] <= right[j]) {
            result.push(left[i++]);
        } else {
            result.push(right[j++]);
        }
    }
    return result.concat(left.slice(i)).concat(right.slice(j));
}

const arr = [6, 3, 8, 2, 7, 4];
const sorted = mergeSort(arr);
console.log("Sorted array is:", sorted);

Time complexity

CaseTimeExplanation
best caseO(n log n)Merge Sort always divides the array into halves and merges them, regardless of the input order. Even if the array is already sorted, it still performs the same number of comparisons and merges.
average caseO(n log n)In the average case, the array is recursively divided into halves (log n levels), and merging at each level takes O(n) time. Therefore, total time complexity is O(n log n).
worst caseO(n log n)Even in the worst case, Merge Sort does not change its behavior — it still performs log n levels of merging with O(n) work at each level, leading to O(n log n) time.

Space complexity

O(n)

Merge Sort requires additional space to store temporary subarrays during the merge process. This auxiliary space grows linearly with the input size.