Fetching latest headlines…

Dev

Merge Sort : Sorting in O(n log n) Using Divide and Conquer

Dev.toUnited States · NORTH AMERICA

Why should you care? Sorting is one of the most common problems in programming. You may need to sort: Student marks Product prices Names Search results Timestamps Database records Large datasets A s...

0 views0 likes0 comments

Why should you care?

Sorting is one of the most common problems in programming.

You may need to sort:

  • Student marks
  • Product prices
  • Names
  • Search results
  • Timestamps
  • Database records
  • Large datasets

A simple sorting algorithm might work well for a small array, but performance becomes important when the input grows.

Merge Sort can sort n elements in:

O(n log n)

More importantly, Merge Sort introduces a powerful problem-solving technique:

Divide the problem into smaller problems, solve them, and combine the results.

This technique is called Divide and Conquer and appears throughout computer science.

The Problem

Suppose we have:

[38, 27, 43, 3, 9, 82, 10]

We want:

[3, 9, 10, 27, 38, 43, 82]

A straightforward approach is to repeatedly find the smallest element and place it in the correct position.

But as the input grows, some sorting algorithms become very slow.

For example, a quadratic algorithm has:

O(n²)

complexity.

Merge Sort improves this to:

O(n log n)

The key idea is that instead of trying to sort the entire array at once, we repeatedly divide it into smaller pieces.

The Concept

Merge Sort follows three major steps:

Divide
  ↓
Conquer
  ↓
Merge

1. Divide

Split the array into two halves.

[38, 27, 43, 3, 9, 82, 10]

          ↓

[38, 27, 43]    [3, 9, 82, 10]

Continue dividing:

[38, 27, 43]
      ↓
[38] [27, 43]
         ↓
      [27] [43]

Eventually every part contains one element.

2. Conquer

A single-element array is already sorted.

[38]
[27]
[43]

Now we begin combining them.

3. Merge

Merge two sorted arrays into one sorted array.

For example:

[27] + [43]

becomes:

[27, 43]

Then:

[38] + [27, 43]

becomes:

[27, 38, 43]

This merging process continues until the entire array is sorted.

Simple Explanation

Imagine you have a pile of 1,000 papers that need to be sorted by number.

Instead of sorting all 1,000 papers at once:

  1. Split them into two piles.
  2. Split each pile again.
  3. Keep splitting until each pile has one paper.
  4. Combine small sorted piles.
  5. Continue combining larger sorted piles.
  6. Eventually you get one completely sorted pile.

The clever part is the merge.

When two groups are already sorted, combining them is easy.

For example:

Group A:
[2, 7, 15]

Group B:
[3, 5, 12]

Compare the front elements:

2 vs 3 → take 2
7 vs 3 → take 3
7 vs 5 → take 5
7 vs 12 → take 7
15 vs 12 → take 12

Finally:

[2, 3, 5, 7, 12, 15]

This is the core operation behind Merge Sort.

Real-world Analogy

Imagine two queues of students where each queue is already sorted by height.

Queue A:
Short → Medium → Tall

Queue B:
Short → Medium → Tall

You don't need to completely reorder either queue.

You simply compare the person at the front of each queue.

Take the shorter person.

Then compare the new front positions.

Repeat until both queues are empty.

That's exactly what the merge step does.

Merge Sort creates many small sorted sequences and then efficiently merges them.

Code Example

Let's implement Merge Sort in Java.

public static void mergeSort(int[] arr, int left, int right) {

    if (left >= right) {
        return;
    }

    int mid = left + (right - left) / 2;

    mergeSort(arr, left, mid);
    mergeSort(arr, mid + 1, right);

    merge(arr, left, mid, right);
}

Now we need the merge operation:

public static void merge(
        int[] arr,
        int left,
        int mid,
        int right) {

    int[] temp = new int[right - left + 1];

    int i = left;
    int j = mid + 1;
    int k = 0;

    while (i <= mid && j <= right) {

        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }

    while (i <= mid) {
        temp[k++] = arr[i++];
    }

    while (j <= right) {
        temp[k++] = arr[j++];
    }

    for (int x = 0; x < temp.length; x++) {
        arr[left + x] = temp[x];
    }
}

We can use it like this:

public static void main(String[] args) {

    int[] arr = {
        38, 27, 43, 3, 9, 82, 10
    };

    mergeSort(arr, 0, arr.length - 1);

    for (int value : arr) {
        System.out.print(value + " ");
    }
}

Output:

3 9 10 27 38 43 82

How the code works

The recursive function first keeps splitting:

[38, 27, 43, 3, 9, 82, 10]

into smaller pieces.

Eventually:

[38] [27] [43] [3] [9] [82] [10]

Then merge() starts combining them.

For example:

[27] + [43]

becomes:

[27, 43]

Then:

[38] + [27, 43]

becomes:

[27, 38, 43]

The same process happens on the other side.

Finally:

[27, 38, 43]
+
[3, 9, 10, 82]

becomes:

[3, 9, 10, 27, 38, 43, 82]

Visualizing Merge Sort

The complete process looks like this:

Starting array:

[38, 27, 43, 3, 9, 82, 10]

Divide

             [38 27 43 3 9 82 10]
                    /       \
             [38 27 43]   [3 9 82 10]
              /    \        /      \
           [38]  [27 43]  [3 9]  [82 10]
                  / \      / \     / \
                [27][43] [3][9] [82][10]

Now every piece is individually sorted.

Merge

[27] + [43]
      ↓
[27 43]

[3] + [9]
      ↓
[3 9]

[82] + [10]
       ↓
[10 82]

Continue:

[38] + [27 43]
        ↓
[27 38 43]

[3 9] + [10 82]
        ↓
[3 9 10 82]

Finally:

[27 38 43]
      +
[3 9 10 82]

        ↓

[3 9 10 27 38 43 82]

Time Complexity

Merge Sort has:

O(n log n)

time complexity.

Why?

There are two important parts.

Number of levels

Every time we divide the array, its size is approximately halved.

Therefore, the number of levels is:

log n

Work at each level

At every level, all elements are processed during merging.

That's:

O(n)

work.

Therefore:

O(n) × O(log n)

gives:

O(n log n)

This applies to the best, average, and worst cases for the standard Merge Sort algorithm.

Case Time
Best O(n log n)
Average O(n log n)
Worst O(n log n)

Space Complexity

The implementation above creates temporary arrays during merging.

Therefore, its auxiliary space complexity is:

O(n)

The recursive calls also require stack space:

O(log n)

But the temporary merge arrays dominate the additional memory usage.

So the typical overall auxiliary space is:

O(n)

This is one of the major trade-offs of Merge Sort:

Excellent time complexity
        ↓
O(n log n)

But

Additional memory required
        ↓
O(n)

Common Mistakes

Mistake 1: Forgetting the base case

Recursive Merge Sort needs to stop when the range contains one element.

if (left >= right) {
    return;
}

Without this condition, the recursion never terminates.

Mistake 2: Incorrectly calculating the middle

Avoid:

int mid = (left + right) / 2;

For large indexes, addition can overflow.

Prefer:

int mid = left + (right - left) / 2;

This is the same safe calculation we saw with Binary Search.

Mistake 3: Forgetting the remaining elements

During merging, one side may become empty first.

For example:

Left:
[2, 5, 8]

Right:
[3]

After selecting:

2
3

the left side still contains:

5, 8

These elements must be copied into the result.

That's why we need:

while (i <= mid) {
    temp[k++] = arr[i++];
}

and:

while (j <= right) {
    temp[k++] = arr[j++];
}

Mistake 4: Thinking the merge operation sorts arbitrary arrays

The merge step assumes that both input portions are already sorted.

For example:

[2, 7, 10]
[1, 5, 9]

can be efficiently merged.

But:

[7, 2, 10]
[9, 1, 5]

cannot simply be merged correctly without first sorting those portions.

That's why Merge Sort works from the bottom up: smaller pieces become sorted before larger pieces are merged.

Advanced Notes

1. Merge Sort is Stable

A sorting algorithm is called stable if equal elements maintain their original relative order.

Consider:

(John, 90)
(Alex, 90)

If sorting by marks, a stable algorithm keeps:

John, 90
Alex, 90

in their original relative order.

Our merge implementation uses:

if (arr[i] <= arr[j])

rather than:

if (arr[i] < arr[j])

This allows the element from the left half to be selected first when values are equal.

Therefore, this implementation is stable.

2. Merge Sort vs Quick Sort

Both are important O(n log n) sorting algorithms.

Feature Merge Sort Quick Sort
Average Time O(n log n) O(n log n)
Worst Time O(n log n) O(n²)
Stable Yes Usually No
Extra Space O(n) Typically O(log n) stack
Main Idea Divide + Merge Divide around Pivot

The biggest difference is the strategy.

Merge Sort divides the array and then performs a carefully controlled merge.

Quick Sort chooses a pivot and partitions the elements around it.

3. Bottom-Up Merge Sort

The implementation we've seen is top-down Merge Sort.

It starts with the entire array:

[entire array]

and recursively divides it.

There is another approach called bottom-up Merge Sort.

It starts with individual elements:

[38] [27] [43] [3] [9] [82] [10]

Then merges pairs:

[27 38] [3 43] [9 82] [10]

Then:

[3 27 38 43] [9 10 82]

And finally:

[3 9 10 27 38 43 82]

It avoids recursion and can be useful in certain implementations.

4. Merge Sort on Linked Lists

Merge Sort is particularly well suited to linked lists.

Why?

Linked lists don't provide efficient random access.

An algorithm that constantly needs:

arr[mid]

is not ideal for a linked list.

But Merge Sort primarily requires:

  • Splitting the list
  • Traversing nodes
  • Merging sorted lists

These operations work naturally with linked lists.

Therefore, Merge Sort is a common choice for sorting linked lists.

5. External Sorting

What if the dataset is too large to fit into memory?

Suppose you have:

500 GB of data

but only:

16 GB RAM

You cannot load everything into memory at once.

External Merge Sort can:

  1. Read manageable chunks.
  2. Sort each chunk.
  3. Store sorted chunks.
  4. Merge those sorted chunks.

This makes Merge Sort useful for large-scale data processing and external storage.

The Bigger Picture

Merge Sort connects several concepts we've already learned.

Big-O

We just learned that:

O(n log n)

is generally much more scalable than:

O(n²)

Merge Sort is one of the classic algorithms that achieves O(n log n) sorting.

Binary Search

Binary Search taught us the power of:

Divide the search space.

Merge Sort applies a related idea:

Divide the problem.
Solve smaller problems.
Combine the results.

Recursion

Merge Sort is a classic example of recursion.

A large problem becomes smaller versions of the same problem:

sort(large array)
       ↓
sort(left half)
sort(right half)
       ↓
merge

Divide and Conquer

This is the deeper lesson.

The pattern is:

                 Problem
                    ↓
              Divide it
              /       \
         Subproblem  Subproblem
              \       /
               Solve
                 ↓
                Merge

This strategy appears in many algorithms beyond sorting.

The Most Important Mental Model

Don't memorize Merge Sort as a collection of recursive function calls.

Remember:

Split until the pieces are easy, then merge them back in sorted order.

The entire algorithm can be summarized as:

        [8 3 5 4 7 6 1 2]
                 ↓
          Split repeatedly
                 ↓
       [8] [3] [5] [4] [7] [6] [1] [2]
                 ↓
          Merge sorted pairs
                 ↓
       [3 8] [4 5] [6 7] [1 2]
                 ↓
          Merge larger groups
                 ↓
       [3 4 5 8] [1 2 6 7]
                 ↓
             Final merge
                 ↓
       [1 2 3 4 5 6 7 8]

The algorithm doesn't magically know where every element belongs.

It makes the problem manageable by ensuring that every merge combines two already-sorted sequences.

Summary

Merge Sort is a comparison-based sorting algorithm based on Divide and Conquer.

The process is:

Divide
  ↓
Sort smaller pieces
  ↓
Merge

Important points:

  • It repeatedly divides the array into halves.
  • Single-element arrays are considered sorted.
  • Sorted portions are merged together.
  • Time complexity is O(n log n) in best, average, and worst cases.
  • Standard implementations require O(n) auxiliary space.
  • Merge Sort is stable.
  • It works particularly well with linked lists.
  • It can be adapted for external sorting.
  • It teaches the important Divide and Conquer technique.

The progression is:

Binary Search
     ↓
Divide the search space
     ↓
Merge Sort
     ↓
Divide the problem
     ↓
Solve smaller problems
     ↓
Combine the results

Comments (0)

Sign in to join the discussion

Be the first to comment!