∑GATE DA Atlas

Sorting — Selection, Bubble, Insertion, Merge Sort and Quicksort

The three elementary sorts with exact comparison and swap counts, inversions and adaptivity, stability and in-place sorting, merge sort (top-down, merging, recurrence, space), quicksort (Lomuto and Hoare partitions, pivot choice, best/average/worst case), the Ω(n log n) lower bound for comparison sorting, and a comparison table to answer "which sort?" questions.

Section 411 practice questions

Vocabulary

TermMeaning
In placeuses O(1)O(1) (or O(log⁡n)O(\log n)) extra memory
Stableequal keys keep their relative order — needed when sorting by several keys in turn
Adaptivefaster on nearly sorted input
Inversiona pair i<ji < j with ai>aja_i > a_j; 00 for sorted, (n2)\binom n2 for reversed, n(n−1)4\frac{n(n-1)}{4} on average
Comparison sortonly learns about the keys by comparing pairs

Selection sort

Repeatedly select the minimum of the unsorted suffix and swap it to the front.

Python
def selection_sort(a):
    n = len(a)
    for i in range(n - 1):
        m = i
        for j in range(i + 1, n):
            if a[j] < a[m]:
                m = j
        a[i], a[m] = a[m], a[i]
    return a
  • Comparisons: always n(n−1)2\frac{n(n-1)}{2}. Swaps: at most n−1n - 1 (the fewest of any simple sort — useful when writes are expensive).
  • Θ(n2)\Theta(n^2) in every case; in place; not stable; not adaptive.

Bubble sort

Sweep left to right, swapping adjacent out-of-order pairs; after pass kk the kk largest elements are in their final places.

Python
def bubble_sort(a):
    n = len(a)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):
            if a[j] > a[j + 1]:
                a[j], a[j + 1] = a[j + 1], a[j]
                swapped = True
        if not swapped:          # early exit: makes the best case O(n)
            break
    return a
  • Swaps = number of inversions. Comparisons: worst n(n−1)2\frac{n(n-1)}{2}; best n−1n - 1 with the early-exit flag.
  • In place, stable, adaptive (with the flag). Rarely the right choice in practice.

Insertion sort

Grow a sorted prefix; insert each new element into place by shifting larger elements right.

Python
def insertion_sort(a):
    for i in range(1, len(a)):
        key = a[i]
        j = i - 1
        while j >= 0 and a[j] > key:
            a[j + 1] = a[j]      # shift
            j -= 1
        a[j + 1] = key
    return a
  • Shifts = inversions; comparisons between n−1n - 1 (sorted) and n(n−1)2\frac{n(n-1)}{2} (reversed).
  • Best O(n)O(n), average and worst O(n2)O(n^2); in place, stable, adaptive; excellent for small or nearly sorted arrays, which is why hybrid sorts (Timsort, introsort) use it for small runs.

Merge sort (divide and conquer)

Split in half, sort each half recursively, merge the two sorted halves.

Python
def merge_sort(a):
    if len(a) <= 1:
        return a
    mid = len(a) // 2
    left, right = merge_sort(a[:mid]), merge_sort(a[mid:])
    out, i, j = [], 0, 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:          # <= keeps it stable
            out.append(left[i]); i += 1
        else:
            out.append(right[j]); j += 1
    return out + left[i:] + right[j:]
  • T(n)=2T(n/2)+Θ(n)=Θ(nlog⁡n)T(n) = 2T(n/2) + \Theta(n) = \Theta(n\log n) in every case; at most n⌈log⁡2n⌉n\lceil\log_2 n\rceil comparisons.
  • Merging lists of sizes mm and nn: between min⁡(m,n)\min(m, n) and m+n−1m + n - 1 comparisons.
  • Needs O(n)O(n) extra space for arrays (not in place); stable; ideal for linked lists and external sorting of data that doesn't fit in memory.
38 27 43 3 9 82 10 38 27 43 3 9 82 10 38 27 43 3 9 82 10 27 38 3 43 9 10 82 3 27 38 43 3 9 10 27 38 43 82
Split down to single elements, then merge sorted runs back up (merges of the right half shown combined).

Quicksort (divide and conquer)

Choose a pivot, partition the array into elements ≤ pivot and > pivot, then recursively sort both sides. All the work is in partitioning; there is no merge step.

Python
def quicksort(a, lo=0, hi=None):
    if hi is None:
        hi = len(a) - 1
    if lo < hi:
        p = partition(a, lo, hi)      # Lomuto: pivot = a[hi]
        quicksort(a, lo, p - 1)
        quicksort(a, p + 1, hi)
    return a
  • Lomuto partition (pivot = last element) is simple; Hoare partition (two pointers moving inward) does about three times fewer swaps.
  • Best/average: Θ(nlog⁡n)\Theta(n\log n) (about 1.39 nlog⁡2n1.39\,n\log_2 n comparisons on average). Worst: Θ(n2)\Theta(n^2) when the pivot is always the smallest or largest — e.g. sorted input with first/last-element pivots, or all-equal keys with a naive partition.
  • In place (O(log⁡n)O(\log n) stack expected, O(n)O(n) worst unless you recurse on the smaller side first); not stable; usually the fastest in practice thanks to cache-friendly scanning.
  • Fixes for bad pivots: random pivot, median-of-three, three-way partitioning for duplicates; introsort switches to heapsort when recursion gets too deep.

Lower bound and non-comparison sorts

Any comparison sort corresponds to a binary decision tree with at least n!n! leaves, so the worst case needs at least ⌈log⁡2n!⌉=Ω(nlog⁡n)\lceil\log_2 n!\rceil = \Omega(n\log n) comparisons (e.g. at least 3 comparisons for 3 elements, 5 for 4 elements).

Counting sort (O(n+k)O(n + k) for keys in 0..k0..k), radix sort and bucket sort avoid comparisons and can run in linear time for suitable keys.

Which sort?

AlgorithmBestAverageWorstExtra spaceStableIn place
Selectionn2n^2n2n^2n2n^2O(1)O(1)noyes
Bubble (with flag)nnn2n^2n2n^2O(1)O(1)yesyes
Insertionnnn2n^2n2n^2O(1)O(1)yesyes
Mergenlog⁡nn\log nnlog⁡nn\log nnlog⁡nn\log nO(n)O(n)yesno
Quicknlog⁡nn\log nnlog⁡nn\log nn2n^2O(log⁡n)O(\log n)noyes
Heapnlog⁡nn\log nnlog⁡nn\log nnlog⁡nn\log nO(1)O(1)noyes
Python sorted (Timsort)nnnlog⁡nn\log nnlog⁡nn\log nO(n)O(n)yesno

Quick revision

Practice questions

GATE-style questions: 4 one-mark and 7 two-mark. Wrong MCQs cost ⅓ of their marks in the real exam; MSQ and NAT have no negative marking. Attempt each before opening the solution.

Q1NAT1 mark

Selection sort is run on an array of 10 elements. The number of element comparisons it makes is ______.

Q2NAT2 marks

The number of swaps performed by bubble sort (swapping adjacent out-of-order elements) to sort [3, 1, 4, 2] in ascending order is ______.

Q3MSQ1 mark

Which of the following sorting algorithms are stable in their standard implementations?

One or more options are correct.

Q4NAT2 marks

Insertion sort is run on an already sorted array of 8 elements. The number of element comparisons is ______.

Q5MCQ2 marks

Quicksort that always picks the first element as pivot is run on an array that is already sorted. Its running time is

Q6MCQ2 marks

What does this program print?

Python
def partition(a, lo, hi):
    pivot = a[hi]
    i = lo - 1
    for j in range(lo, hi):
        if a[j] <= pivot:
            i += 1
            a[i], a[j] = a[j], a[i]
    a[i + 1], a[hi] = a[hi], a[i + 1]
    return i + 1

a = [4, 7, 2, 9, 1, 5]
p = partition(a, 0, len(a) - 1)
print(p, a)
Q7NAT2 marks

The maximum number of comparisons needed to merge two sorted lists of 4 elements each into one sorted list is ______.

Q8MCQ1 mark

Any comparison-based sorting algorithm needs, in the worst case, at least

Q9NAT2 marks

Bubble sort (ascending, comparing neighbours left to right) is applied to [5, 1, 4, 2, 8]. The number of swaps made during the first pass is ______.

Q10MSQ2 marks

Which statements are correct?

One or more options are correct.

Q11NAT1 mark

Insertion sort is applied to [5, 4, 3, 2, 1]. The total number of element shifts (moves of an element one position to the right) is ______.