Merge Sort Algorithm
Imagine you have to sort 1,000 exam papers by roll number. You could pick up one paper at a time and search the pile for its place. That works, but it gets slow very quickly. A smarter plan is to split the pile between friends, let each friend sort a smaller pile, and then combine the sorted piles into one. Merge Sort works in exactly this way.
A sorting algorithm is a set of steps that puts items in order, such as smallest to largest or A to Z. Sorting matters because ordered data is easier to search, compare, and process. Binary search only works on sorted data, and tasks like finding duplicates become much faster once the data is in order.
Merge Sort is a sorting algorithm built on the divide-and-conquer technique. It splits an array into halves until every piece holds a single element, then merges the pieces back together in sorted order. It runs in O(n log n) time in every case, which is why people consider it one of the dependable sorting algorithms. Watch it run below, then read on to see why it works.
Interactive Merge Sort Visualization
The animation starts by itself. Pause it at any time, then use Previous and Next to move one step at a time. You can also type your own array.
- Unprocessed
- Left part (L)
- Right part (R)
- Being compared
- Taken (lifted, outlined)
- Already used
- Sorted (✓)
Merged Result
Current Operation
Recursion Tree (built as the algorithm runs)
Sorting Complete
Sorted Array:
Time Complexity: O(n log n)
Space Complexity: O(n)
These are the general complexities of Merge Sort. This demo counts steps. It does not measure real running time.
What Is Merge Sort?
Merge Sort is a sorting algorithm that sorts an array by splitting it into smaller parts, sorting those parts, and merging them back together. It repeats four actions:
- Divide the array into two smaller parts.
- Keep dividing until every part contains one element.
- Merge those small parts back together.
- While merging, place the elements in sorted order.
The key fact is this: a single element is already sorted. One item cannot be out of order with itself. Merge Sort uses this to turn a hard problem into many trivial ones. Here is a tiny example:
[8, 3, 5, 1]
split → [8, 3] [5, 1]
split → [8] [3] [5] [1]
merge → [3, 8] [1, 5]
merge → [1, 3, 5, 8]
The array splits into pairs, then into single elements. Merging goes the other way. [8] and [3] become [3, 8]. [5] and [1] become [1, 5]. Finally the two sorted pairs merge into [1, 3, 5, 8].
The Main Idea Behind Merge Sort
Merge Sort follows three stages: divide, conquer, and combine.
- Divide: Split the array into two smaller arrays.
- Conquer: Sort each half. The algorithm does this by calling itself on each half, which is called recursion.
- Combine: Merge the two sorted halves into one sorted array.
The combine step is where the real sorting happens. Splitting only cuts the array in the middle and never moves any values. The merge step is cheap because both halves are already sorted. To merge them, you only need to look at the front of each half, since the smallest remaining element in each half always sits at its front. You never have to search through a half.
How Merge Sort Works
Let us sort [38, 27, 43, 3, 9, 82, 10]. The array has 7 elements, so the left half gets 3 elements (7 // 2 = 3) and the right half gets the other 4. This is how the array splits, level by level:
Level 0: [38, 27, 43, 3, 9, 82, 10]
Level 1: [38, 27, 43] [3, 9, 82, 10]
Level 2: [38] [27, 43] [3, 9] [82, 10]
Level 3: [38] [27] [43] [3] [9] [82] [10]
Every part at level 3 has one element, so no more splitting is needed. Odd lengths cause no trouble. One half is simply one element bigger than the other.
Now the algorithm merges upward. It retraces the same levels in reverse:
Level 3 → 2: [38] [27, 43] [3, 9] [10, 82]
Level 2 → 1: [27, 38, 43] [3, 9, 10, 82]
Level 1 → 0: [3, 9, 10, 27, 38, 43, 82]
The final result is [3, 9, 10, 27, 38, 43, 82]. Notice that [38] waited alone until the level 2 merge. Not every part merges at every level.
Divide and Conquer
Divide and conquer is a way to solve big problems. You break the problem into smaller versions of itself, solve the small versions, and then use their answers to build the answer to the big problem. The smaller problems must look like the original, so the same method works on them again.
| Stage | General idea | In Merge Sort |
|---|---|---|
| Divide | Break a large problem into smaller problems. | Split the array at the middle. |
| Conquer | Solve the smaller problems. | Sort each half by calling Merge Sort again. One-element parts are already sorted. |
| Combine | Join the solutions into the final answer. | Merge the two sorted halves into one sorted array. |
Binary search, Quick Sort, and many other algorithms use the same pattern. Merge Sort is a popular first example because each stage is easy to see: the divide step is trivial, and all the work sits in the combine step.
Step-by-Step Example
We use the same array again: [38, 27, 43, 3, 9, 82, 10].
- Stage 1:
[38, 27, 43, 3, 9, 82, 10] - Stage 2: split into
[38, 27, 43]and[3, 9, 82, 10] - Stage 3: split again into
[38][27, 43][3, 9][82, 10] - Stage 4: split until only single elements remain:
[38][27][43][3][9][82][10] - Stage 5: merge neighbors:
[27, 43],[3, 9], and[10, 82] - Stage 6: merge again:
[27, 38, 43]and[3, 9, 10, 82] - Stage 7: merge the last two parts:
[3, 9, 10, 27, 38, 43, 82]
The stages above show the idea level by level, but the program does not work that way. It works depth first: it finishes the whole left side before it touches the right side. In the visualization you can see this order of merges:
- Merge [27] and [43] into [27, 43].
- Merge [38] and [27, 43] into [27, 38, 43]. The left half is now sorted.
- Merge [3] and [9] into [3, 9].
- Merge [82] and [10] into [10, 82].
- Merge [3, 9] and [10, 82] into [3, 9, 10, 82]. The right half is now sorted.
- Merge [27, 38, 43] and [3, 9, 10, 82] into the final array.
Merge number 6 does the most work. It compares 27 with 3 and takes 3. Then it compares 27 with 9 and takes 9, compares 27 with 10 and takes 10, and compares 27 with 82 and takes 27. Next, 38 beats 82 and 43 beats 82. Only 82 remains in the right half, so it goes to the end.
How the Merge Process Works
This is the most important part of Merge Sort, and the visualization shows it clearly. Suppose we need to merge two sorted arrays:
Left: [3, 27, 38]
Right: [9, 10, 43]
Merge Sort does not just glue the two arrays together. Gluing would give [3, 27, 38, 9, 10, 43], which is not sorted. Instead, it compares the smallest remaining element of each array and takes the smaller one.
| Step | Compare | Take | Merged so far |
|---|---|---|---|
| 1 | 3 vs 9 | 3 | [3] |
| 2 | 27 vs 9 | 9 | [3, 9] |
| 3 | 27 vs 10 | 10 | [3, 9, 10] |
| 4 | 27 vs 43 | 27 | [3, 9, 10, 27] |
| 5 | 38 vs 43 | 38 | [3, 9, 10, 27, 38] |
| 6 | Left is empty | 43 | [3, 9, 10, 27, 38, 43] |
The method works because each array is already sorted. The front element of an array is its smallest, so the smaller of the two front elements is the smallest of everything left. Taking it is always safe.
The merge uses one pointer for each array. Move a pointer forward only when you take an element from that array. When one array runs out, copy everything left in the other array to the end. Since it is already sorted, nothing else needs checking.
When two values are equal, take the one from the left array first. This keeps equal values in their original order, and it makes Merge Sort stable.
Merge Sort Pseudocode
MERGE_SORT(array)
IF length(array) <= 1
RETURN array
middle ← length(array) / 2 (rounded down)
left ← MERGE_SORT(first half of array)
right ← MERGE_SORT(second half of array)
RETURN MERGE(left, right)
MERGE(left, right)
result ← empty array
WHILE left is not empty AND right is not empty
IF first element of left <= first element of right
remove first element of left and add it to result
ELSE
remove first element of right and add it to result
add all remaining elements of left to result
add all remaining elements of right to result
RETURN result
Merge Sort in Python
def merge_sort(arr):
# Base case: 0 or 1 elements are already sorted
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 = 0
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
numbers = [38, 27, 43, 3, 9, 82, 10]
print(merge_sort(numbers)) # [3, 9, 10, 27, 38, 43, 82]
Line-by-Line Explanation
Recursion in plain words
Recursion means a function calls itself on a smaller version of the same job. Think of a manager who gets a stack of files to sort. She keeps none of the work. She cuts the stack in half, hands one half to each assistant, and waits. Each assistant does the same with a smaller stack. Eventually someone receives a stack of one file and hands it straight back, because one file is already in order. Then each manager merges the two sorted stacks she receives.
The base case
if len(arr) <= 1: return arr stops the recursion. An array with zero or one element is already sorted, so the function returns it unchanged. Without this line, the function would call itself forever.
The middle index
mid = len(arr) // 2 finds the split point. The // operator divides and rounds down, so 7 elements give mid = 3.
The recursive calls
arr[:mid] is the left half: everything before index mid. arr[mid:] is the right half: everything from index mid onward. Each half goes into merge_sort again, and the function returns a sorted version of it. By the time both calls finish, left and right are sorted.
The merge function
merge takes two sorted lists and builds one. It starts with an empty list called result.
The pointers i and j
i tracks the next unused element in left. j does the same for right. Both start at 0.
The comparison
The loop runs while both lists still have unused elements. Each round compares left[i] with right[j]. The <= sign means that on a tie, the left element wins. That choice keeps the sort stable.
Appending elements
The winner goes into result with append, and only its pointer moves forward. The other pointer stays put, because that element has not been used yet.
The remaining elements
When the loop ends, at least one list is used up. The other may still hold elements. left[i:] and right[j:] are the unused tails. One of them is empty, and the other is already sorted and larger than everything in result, so extend can add it as it is.
The final result
return result hands the merged, sorted list back to the caller. That caller is either another merge step higher up the recursion or, at the very top, your finished answer.
Time Complexity
Merge Sort runs in O(n log n) time. Two ideas explain where the two parts come from.
The log n part. The array splits into two halves each time, so the number of levels is about log₂(n). An array of 8 elements needs 3 levels of splitting (8 → 4 → 2 → 1). An array of 1,000,000 elements needs only about 20.
The n part. At every level, the merge steps together touch all n elements once. Some merges are small and some are large, but their sizes add up to n.
| Level (n = 8) | Parts | Size of each part | Elements merged at this level |
|---|---|---|---|
| Top merge | 1 | 8 | 8 |
| Next | 2 | 4 | 8 |
| Next | 4 | 2 | 8 |
| Bottom (single elements) | 8 | 1 | no merging needed |
Three merge levels with about 8 elements each give about 24 element moves, which is 8 × log₂(8). In general:
O(n) work per level × O(log n) levels = O(n log n)
| Case | Time |
|---|---|
| Best case | O(n log n) |
| Average case | O(n log n) |
| Worst case | O(n log n) |
All three are the same for standard Merge Sort because the algorithm always splits and always merges, no matter what the data looks like. An already sorted array does need fewer comparisons, since one half runs out early. Still, the algorithm splits and copies the same number of elements, so the total time stays proportional to n log n.
Space Complexity
The standard array-based version of Merge Sort needs O(n) extra memory. The reason is the merge step. It cannot safely write the merged values into the same spots it is still reading from, so it builds the result in a temporary array of the same total size as the parts being merged. The recursion also uses a call stack that is O(log n) deep, but that is small next to the O(n) temporary storage.
Implementation details change the exact numbers. The Python code above creates new lists with slicing, while other versions reuse one helper array. Both are O(n) in extra space. Truly in-place versions exist, but they are much more complicated and rarely worth the effort.
Merge Sort vs Quick Sort
| Feature | Merge Sort | Quick Sort |
|---|---|---|
| Basic strategy | Split at the middle, sort each half, merge | Choose a pivot, partition around it, sort each side |
| Best-case time | O(n log n) | O(n log n) |
| Average-case time | O(n log n) | O(n log n) |
| Worst-case time | O(n log n) | O(n²) with poor pivots |
| Additional memory | O(n) for arrays | Usually O(log n) for the call stack |
| Stable? | Yes, when ties take the left element | Usually not |
| Recursive? | Usually, but a bottom-up loop version exists | Usually, but it can be written with a stack |
| Typical use | Linked lists, external sorting, stable sorting | In-memory arrays |
| Performance traits | Steady and predictable; extra copying | Often fast in practice; sensitive to pivot choice |
Neither algorithm is better in every situation. Quick Sort works in place and often performs well on in-memory arrays, but a bad pivot choice can slow it down. Merge Sort gives steady timing and stability, but it needs extra memory. Real performance depends on your implementation, your data, your memory limits, and the hardware you run on.
Merge Sort vs Bubble Sort
| Feature | Merge Sort | Bubble Sort |
|---|---|---|
| Approach | Divide, sort halves, merge | Swap neighboring elements that are out of order, again and again |
| Best case | O(n log n) | O(n) with an early-exit check |
| Average case | O(n log n) | O(n²) |
| Worst case | O(n log n) | O(n²) |
| Space | O(n) | O(1) |
| Practical use | Real sorting jobs, large data | Teaching and very small inputs |
The gap shows up as the data grows. For 1,000,000 elements, n log₂ n is about 20 million steps, while n² is about one trillion. Merge Sort finishes in moments, while Bubble Sort would take hours. That is why Merge Sort scales to large datasets and Bubble Sort does not.
Advantages of Merge Sort
- Guaranteed O(n log n) time. The worst case is as good as the average case.
- Predictable performance. The running time hardly depends on how the input is arranged.
- Stable. When implemented with the
<=rule, equal elements keep their original order. This matters when you sort records by one field and want an earlier order preserved. - Handles large datasets well. The work grows only slightly faster than the input size.
- Suits linked lists. Merging needs only sequential access, so linked lists can be merged with almost no extra memory.
- Useful for external sorting. Data too large for memory can be sorted in chunks on disk and merged.
- A clear divide-and-conquer example. Learning it prepares you for many other algorithms.
Limitations of Merge Sort
- Extra memory. The standard array version needs O(n) additional storage, unlike in-place algorithms.
- More complex than simple sorts. It has more moving parts than Bubble Sort or Insertion Sort.
- Recursion can confuse beginners. Following several calls at once takes practice.
- Overhead on small inputs. For very small arrays, a simple algorithm can be faster because it avoids the copying and function calls. Many libraries switch to Insertion Sort for tiny parts.
- Copying costs time. Moving elements in and out of temporary arrays adds a constant cost that in-place methods avoid.
When Should You Use Merge Sort?
Merge Sort is a good choice in these situations:
- Large datasets where an O(n²) algorithm would be far too slow.
- Strict time guarantees. If you cannot risk a slow worst case, O(n log n) in every case helps.
- Stability matters. For example, when sorting people by city after sorting them by name.
- External sorting of files that do not fit in memory.
- Linked lists, where merging is natural and cheap.
- Learning. It is one of the clearest ways to understand divide and conquer.
It is not always the best tool. If memory is tight and stability does not matter, an in-place algorithm may suit you better. For a handful of elements, a simple sort is fine.
Conclusion
Merge Sort solves a large sorting problem by breaking it into tiny ones. It splits the array until single elements remain, then merges sorted parts by always taking the smaller front element. This gives it O(n log n) time in every case, at the price of O(n) extra memory. The animation at the top of this page shows both halves of the process, so replay it with your own arrays until the split-and-merge pattern feels natural. Then try writing the Python version from memory.
Frequently Asked Questions
What is Merge Sort?
Merge Sort is a divide-and-conquer sorting algorithm. It splits an array into halves, sorts each half, and merges the sorted halves into one sorted array.
How does Merge Sort work?
It divides the array until each part has one element. Then it repeatedly merges two sorted parts by comparing their front elements and taking the smaller one, until one sorted array remains.
What is the time complexity of Merge Sort?
O(n log n) in the best, average, and worst cases. There are about log₂(n) levels, and each level does about n work.
What is the space complexity of Merge Sort?
The standard array-based version uses O(n) extra space for temporary arrays. The recursion adds an O(log n) call stack.
Is Merge Sort stable?
Yes, when the merge takes the left element on a tie (the <= comparison). Equal values then keep their original order.
Is Merge Sort better than Quick Sort?
Not always. Merge Sort has a guaranteed O(n log n) worst case and is stable, but it needs extra memory. Quick Sort sorts in place and is often fast in practice, but its worst case is O(n²). The better choice depends on your data and constraints.
Why does Merge Sort use recursion?
Each half of the array is a smaller sorting problem of the same kind, so the algorithm can call itself on each half. Recursion also lets the splitting stop cleanly at the one-element base case. You can also write Merge Sort with loops, from the bottom up.
Can Merge Sort work with duplicate values?
Yes. Duplicates cause no problem. With the <= rule, equal values stay in their original order. Try 5,5,3,3,1 in the visualization to see it.
