Free tutorials & notes in Hindi & English · Clean code examples · Mobile friendly learning
📘 Lesson  ·  Lesson 79

Merge Sort

Written and reviewed by · Senior IT Faculty · 15+ years’ experience

Merge Sort and Divide-and-Conquer

Merge sort divides a sequence into two smaller ranges, recursively sorts each range, and merges the two sorted results. The base case is a range of zero or one element. Unlike elementary quadratic sorts, its running time remains Θ(n log n) even for reverse-ordered input.

  1. Divide: choose the midpoint of [left,right).
  2. Conquer: sort [left,middle) and [middle,right).
  3. Combine: merge both ordered ranges into one ordered range.

Half-open ranges make lengths, empty ranges and adjacent partitions precise. Review recursion and compare with bubble sort.

Recursive Trace

For {38,27,43,3,9,82,10}, the conceptual recursion tree is:

[38 27 43 3 9 82 10]
├─ [38 27 43]       → [27 38 43]
│  ├─ [38]          → [38]
│  └─ [27 43]       → [27 43]
└─ [3 9 82 10]      → [3 9 10 82]
   ├─ [3 9]         → [3 9]
   └─ [82 10]       → [10 82]
final merge          → [3 9 10 27 38 43 82]

Splitting does not perform the ordering work. Order is established while recursion returns and sorted child ranges are merged.

How the Merge Procedure Works

Maintain indices into the left range, right range and buffer. Copy the smaller front element, advance that range, then copy any remaining tail. Finally copy the merged buffer segment back.

while both ranges have elements
    copy the smaller front value to buffer
copy remaining left values
copy remaining right values
copy buffer segment back to values

When values are equal, taking from the left range first preserves their original relative order. That one comparison choice—<= rather than <—is central to stability.

Complete Merge Sort Program with One Reusable Buffer

#include <iostream>
#include <vector>

void merge(std::vector<int>& values, std::vector<int>& buffer,
           std::size_t left, std::size_t middle, std::size_t right) {
    std::size_t i = left, j = middle, k = left;
    while (i < middle && j < right) {
        if (values[i] <= values[j]) buffer[k++] = values[i++];
        else                         buffer[k++] = values[j++];
    }
    while (i < middle) buffer[k++] = values[i++];
    while (j < right)  buffer[k++] = values[j++];
    for (std::size_t p = left; p < right; ++p) values[p] = buffer[p];
}

void merge_sort_range(std::vector<int>& values,
                      std::vector<int>& buffer,
                      std::size_t left, std::size_t right) {
    if (right - left < 2) return;
    const std::size_t middle = left + (right - left) / 2;
    merge_sort_range(values, buffer, left, middle);
    merge_sort_range(values, buffer, middle, right);
    merge(values, buffer, left, middle, right);
}

void merge_sort(std::vector<int>& values) {
    std::vector<int> buffer(values.size());
    merge_sort_range(values, buffer, 0, values.size());
}

int main() {
    std::vector<int> values{38, 27, 43, 3, 9, 82, 10};
    merge_sort(values);
    std::cout << "Sorted:";
    for (int value : values) std::cout << ' ' << value;
    std::cout << '\n';
}
Sorted: 3 9 10 27 38 43 82

The buffer is allocated once, not at every recursive call. Midpoint calculation avoids adding two potentially large indices, and the half-open representation avoids middle+1 boundary errors.

Correctness and Stability

Use induction on range length. A range shorter than two is sorted. Assume both recursive calls correctly sort their shorter ranges. The merge procedure repeatedly selects the least unconsumed front value, so its output is ordered; every input element is copied exactly once, so the result is a permutation of the input. Therefore the parent range is sorted.

Stability matters for records sorted in stages. If two records have the same key and the left one originally appeared first, selecting left on equality retains that order. Reversing the tie choice makes this implementation unstable.

Time and Space Complexity

PropertyArray merge sort
Best/average/worst timeΘ(n log n)
Merge work per levelΘ(n)
Recursion depthΘ(log n)
Auxiliary bufferΘ(n)
StableYes, with left-first ties

The recurrence is T(n)=2T(n/2)+Θ(n). An optional check can skip merging when values[middle-1] <= values[middle]; that helps ordered regions but does not alter the standard worst-case bound.

Use Cases and Algorithm Choice

Merge sort offers predictable performance and natural stability. Its sequential merge pattern is useful for external data organized in sorted runs, and linked structures can merge by relinking nodes. For an ordinary C++ vector, library algorithms are normally preferable.

RequirementChoice
Fast general vector sortstd::sort
Stable standard-library sortstd::stable_sort
Predictable educational Θ(n log n)Merge sort
Tiny/nearly sorted inputInsertion sort can be simpler

See C++ algorithms and sorting/searching for production interfaces.

Common Mistakes, Testing and Practice

MistakeCorrection
No base caseReturn for length below two
Mixed inclusive/exclusive indicesUse [left,right) consistently
Forgetting remaining tailCopy both possible leftovers
Allocating arrays in every callReuse one buffer
Right-first equalityChoose left first for stability
Variable-length C arraysUse standard C++ containers

Test empty, one item, odd/even lengths, sorted, reverse, duplicates and negative values. Practice descending order, sorting records by key, counting comparisons, bottom-up iterative merge sort and the already-ordered merge skip.

Authoritative References

The divide-and-conquer definition, merge behavior and standard-library comparison were verified with these references.

Frequently Asked Questions

What is the time complexity of merge sort?
Merge sort takes Θ(n log n) time in the best, average and worst cases because there are logarithmically many split levels and linear merging work per level.
Is merge sort stable?
It can be stable. During merge, choose the left element when keys are equal; the implementation on this page uses less-than-or-equal for that reason.
Is merge sort in-place?
The common array implementation is not: it uses O(n) auxiliary storage plus O(log n) recursion stack. Specialized in-place variants exist but are more complex.
What is the recursion base case?
A half-open range with fewer than two elements is already sorted, so the function returns when right minus left is less than two.
When is merge sort preferred?
It is useful when stable ordering and predictable Θ(n log n) time matter, and it is well suited conceptually to linked-list and external sorting workflows.

Merge Sort और Divide-and-Conquer

Merge sort sequence को दो ranges में बांटता, दोनों को recursively sort करता और फिर sorted results merge करता है। Zero या one-element range base case है। Reverse input पर भी time Θ(n log n) रहता है।

  1. Midpoint पर divide करें।
  2. Left और right range sort करें।
  3. दोनों ordered ranges combine करें।

Recursion और bubble sort से तुलना करें।

Recursive Trace

[38 27 43 3 9 82 10]
left  [38 27 43]  → [27 38 43]
right [3 9 82 10] → [3 9 10 82]
final merge        → [3 9 10 27 38 43 82]

Splitting order नहीं बनाता; returning recursion में sorted child ranges का merge actual ordering करता है।

Merge Procedure

Left, right और buffer के indices रखें। छोटे front element को buffer में copy करें, उस index को आगे बढ़ाएं, फिर बचा tail copy करें। Equal values पर left पहले लेने से stability रहती है।

Complete C++ Program

void merge_sort_range(std::vector<int>& a,
                      std::vector<int>& temp,
                      std::size_t left, std::size_t right) {
    if (right - left < 2) return;
    auto mid = left + (right - left) / 2;
    merge_sort_range(a, temp, left, mid);
    merge_sort_range(a, temp, mid, right);
    merge(a, temp, left, mid, right);
}
Input: 38 27 43 3 9 82 10 Sorted: 3 9 10 27 38 43 82

ऊपर English section का complete compile-ready program एक reusable buffer लेता है। Half-open ranges off-by-one errors कम करती हैं।

Correctness और Stability

Induction से: length 0/1 range sorted है। Recursive calls छोटी ranges sort करती हैं। Merge हर बार least unconsumed front चुनता है, हर element exactly once copy करता है; इसलिए ordered permutation मिलता है। Equal keys पर left पहले चुनने से original relative order बचता है।

Time और Space

PropertyResult
Best/Average/WorstΘ(n log n)
BufferΘ(n)
Recursion depthΘ(log n)
Stableहाँ, left-first tie

हर level में total merge work linear और levels logarithmic हैं।

Use Cases और Comparison

Stable order और predictable performance में merge sort अच्छा है। General vector के लिए std::sort, standard stable sorting के लिए std::stable_sort लें। External sorted runs और linked structures में merge pattern natural है। STL algorithms देखें।

Mistakes, Testing और Practice

  • Base case जरूर रखें।
  • Inclusive और exclusive bounds mix न करें।
  • Left/right leftover tail copy करें।
  • हर recursion में नया buffer न बनाएं।
  • Duplicates से stability test करें।

Empty, odd/even length, sorted, reverse और negative inputs test करें। Bottom-up merge sort practice करें।

Authoritative संदर्भ

Algorithm definition और complexity authoritative sources से verify किए गए हैं।

← Back to C++ Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

💻 Live Code Editor

This page's programs are ready here — run them, edit them, and learn. No installation needed.
Powered by OneCompiler. The code loads into the editor automatically — press Run to see the output. If the editor does not open, open it in a new tab.