Chapter 5: Sorting
CBSE Unit: NOT explicitly in current CBSE syllabus (2025-26), but bubble sort/insertion sort may appear Status: Supplementary, sorting is a fundamental CS concept, likely to be added Priority: PREPARE, bubble sort especially is frequently asked even in current papers
Key Concepts
5.1 What is Sorting?
- Process of arranging elements in a particular order (ascending/descending), Enables efficient searching (binary search requires sorted data), Three sorting algorithms covered: Bubble Sort, Selection Sort, Insertion Sort
- All three are comparison-based, in-place sorting algorithms with O(n^2) worst-case time complexity
5.2 Bubble Sort
Concept, Compare adjacent elements, swap if out of order, After each pass, largest unsorted element "bubbles up" to its correct position, Total passes: n-1 for n elements, Each pass reduces the comparison range by 1 (last elements already sorted)
Algorithm
For i = 0 to n-1:
For j = 0 to n-i-2:
If list[j] > list[j+1]:
Swap list[j] and list[j+1]
Python Implementation (Ascending Order)
def bubble_sort(list1):
n = len(list1)
for i in range(n): # number of passes
for j in range(0, n - i - 1): # compare adjacent
if list1[j] > list1[j + 1]:
list1[j], list1[j + 1] = list1[j + 1], list1[j]
numList = [8, 7, 13, 1, -9, 4]
bubble_sort(numList)
print(numList) # [-9, 1, 4, 7, 8, 13]
Python Implementation (Descending Order)
def bubble_sort_desc(list1):
n = len(list1)
for i in range(n):
for j in range(0, n - i - 1):
if list1[j] < list1[j + 1]: # flip comparison: < instead of >
list1[j], list1[j + 1] = list1[j + 1], list1[j]
numList = [8, 7, 13, 1, -9, 4]
bubble_sort_desc(numList)
print(numList) # [13, 8, 7, 4, 1, -9]
Step-by-Step Dry Run Trace: Bubble Sort
Input: [8, 7, 13, 1, -9, 4] (n = 6)
Pass 1 (i=0): Compare from index 0 to 4
| j | Compare | Action | List After |
|---|---|---|---|
| 0 | 8 > 7? Yes | Swap | [7, 8, 13, 1, -9, 4] |
| 1 | 8 > 13? No | No swap | [7, 8, 13, 1, -9, 4] |
| 2 | 13 > 1? Yes | Swap | [7, 8, 1, 13, -9, 4] |
| 3 | 13 > -9? Yes | Swap | [7, 8, 1, -9, 13, 4] |
| 4 | 13 > 4? Yes | Swap | [7, 8, 1, -9, 4, 13] |
After Pass 1: [7, 8, 1, -9, 4, 13] (13 is in final position)
Pass 2 (i=1): Compare from index 0 to 3
| j | Compare | Action | List After |
|---|---|---|---|
| 0 | 7 > 8? No | No swap | [7, 8, 1, -9, 4, 13] |
| 1 | 8 > 1? Yes | Swap | [7, 1, 8, -9, 4, 13] |
| 2 | 8 > -9? Yes | Swap | [7, 1, -9, 8, 4, 13] |
| 3 | 8 > 4? Yes | Swap | [7, 1, -9, 4, 8, 13] |
After Pass 2: [7, 1, -9, 4, 8, 13] (8 is in final position)
Pass 3 (i=2): Compare from index 0 to 2
| j | Compare | Action | List After |
|---|---|---|---|
| 0 | 7 > 1? Yes | Swap | [1, 7, -9, 4, 8, 13] |
| 1 | 7 > -9? Yes | Swap | [1, -9, 7, 4, 8, 13] |
| 2 | 7 > 4? Yes | Swap | [1, -9, 4, 7, 8, 13] |
After Pass 3: [1, -9, 4, 7, 8, 13] (7 is in final position)
Pass 4 (i=3): Compare from index 0 to 1
| j | Compare | Action | List After |
|---|---|---|---|
| 0 | 1 > -9? Yes | Swap | [-9, 1, 4, 7, 8, 13] |
| 1 | 1 > 4? No | No swap | [-9, 1, 4, 7, 8, 13] |
After Pass 4: [-9, 1, 4, 7, 8, 13] (4 is in final position)
Pass 5 (i=4): Compare from index 0 to 0
| j | Compare | Action | List After |
|---|---|---|---|
| 0 | -9 > 1? No | No swap | [-9, 1, 4, 7, 8, 13] |
Final sorted list: [-9, 1, 4, 7, 8, 13]
Time Complexity
- Worst case: O(n^2), list is reverse sorted
- Best case: O(n), list is already sorted (with optimization)
- Optimization: if no swaps in a pass, list is sorted, stop early
Optimized Bubble Sort
def bubble_sort_optimized(list1):
n = len(list1)
for i in range(n):
swapped = False
for j in range(0, n - i - 1):
if list1[j] > list1[j + 1]:
list1[j], list1[j + 1] = list1[j + 1], list1[j]
swapped = True
if not swapped: # no swaps = already sorted
break
Example where optimization helps: Input: [1, 2, 3, 4, 5] (already sorted), Pass 1: 0 swaps -> swapped = False -> break immediately, Only 1 pass instead of 4 -> O(n) instead of O(n^2)
5.3 Selection Sort
Concept, Find the minimum element in unsorted portion, swap with the leftmost unsorted element, Divides list into: sorted (left) and unsorted (right) portions, Total passes: n-1
- Key characteristic: minimum number of swaps (exactly n-1 swaps maximum)
Algorithm
For i = 0 to n-2:
Find index of minimum element in list[i:n]
Swap list[i] with list[min_index]
Python Implementation (Ascending Order)
def selection_sort(list1):
n = len(list1)
for i in range(n - 1):
min_idx = i
for j in range(i + 1, n):
if list1[j] < list1[min_idx]:
min_idx = j
list1[i], list1[min_idx] = list1[min_idx], list1[i]
numList = [8, 7, 13, 1, -9, 4]
selection_sort(numList)
print(numList) # [-9, 1, 4, 7, 8, 13]
Python Implementation (Descending Order)
def selection_sort_desc(list1):
n = len(list1)
for i in range(n - 1):
max_idx = i
for j in range(i + 1, n):
if list1[j] > list1[max_idx]: # find MAXIMUM instead
max_idx = j
list1[i], list1[max_idx] = list1[max_idx], list1[i]
numList = [8, 7, 13, 1, -9, 4]
selection_sort_desc(numList)
print(numList) # [13, 8, 7, 4, 1, -9]
Step-by-Step Dry Run Trace: Selection Sort
Input: [8, 7, 13, 1, -9, 4] (n = 6)
Pass 1 (i=0): Find minimum in list[0:6]
| j | list[j] | min_idx | list[min_idx] | Action |
|---|---|---|---|---|
| 1 | 7 | 1 | 7 | 7 < 8, update min_idx to 1 |
| 2 | 13 | 1 | 7 | 13 > 7, no change |
| 3 | 1 | 3 | 1 | 1 < 7, update min_idx to 3 |
| 4 | -9 | 4 | -9 | -9 < 1, update min_idx to 4 |
| 5 | 4 | 4 | -9 | 4 > -9, no change |
Swap list[0] with list[4]: [8, 7, 13, 1, -9, 4] -> [-9, 7, 13, 1, 8, 4] Sorted so far: [-9 | 7, 13, 1, 8, 4]
Pass 2 (i=1): Find minimum in list[1:6]
| j | list[j] | min_idx | list[min_idx] |
|---|---|---|---|
| 2 | 13 | 1 | 7 |
| 3 | 1 | 3 | 1 |
| 4 | 8 | 3 | 1 |
| 5 | 4 | 3 | 1 |
Swap list[1] with list[3]: [-9, 7, 13, 1, 8, 4] -> [-9, 1, 13, 7, 8, 4] Sorted so far: [-9, 1 | 13, 7, 8, 4]
Pass 3 (i=2): Find minimum in list[2:6]
| j | list[j] | min_idx | list[min_idx] |
|---|---|---|---|
| 3 | 7 | 3 | 7 |
| 4 | 8 | 3 | 7 |
| 5 | 4 | 5 | 4 |
Swap list[2] with list[5]: [-9, 1, 13, 7, 8, 4] -> [-9, 1, 4, 7, 8, 13] Sorted so far: [-9, 1, 4 | 7, 8, 13]
Pass 4 (i=3): Find minimum in list[3:6]
Min = 7 at index 3. Swap list[3] with list[3] (no actual change). Sorted so far: [-9, 1, 4, 7 | 8, 13]
Pass 5 (i=4): Find minimum in list[4:6]
Min = 8 at index 4. Swap list[4] with list[4] (no actual change).
Final sorted list: [-9, 1, 4, 7, 8, 13]
Total swaps: 3 (much fewer than bubble sort!)
Time Complexity
- All cases: O(n^2), always scans entire unsorted portion
- No best case optimization - unlike bubble sort, selection sort always makes the same number of comparisons
- Advantage: minimum number of swaps (at most n-1)
Selection Sort vs Bubble Sort
| Aspect | Bubble Sort | Selection Sort |
|---|---|---|
| # Swaps | Can be many (up to n^2/2) | At most n-1 |
| Best case | O(n) with optimization | O(n^2) always |
| When to prefer | Nearly sorted data | When swaps are expensive |
5.4 Insertion Sort
Concept, Builds sorted list one element at a time, Takes each element and inserts it at the correct position in the sorted portion, Like sorting playing cards in your hand, Starts from index 1 (first element is considered "already sorted")
Algorithm
For i = 1 to n-1:
key = list[i]
j = i - 1
While j >= 0 and list[j] > key:
list[j+1] = list[j] # shift right
j = j - 1
list[j+1] = key
Python Implementation (Ascending Order)
def insertion_sort(list1):
for i in range(1, len(list1)):
key = list1[i]
j = i - 1
while j >= 0 and list1[j] > key:
list1[j + 1] = list1[j]
j -= 1
list1[j + 1] = key
numList = [8, 7, 13, 1, -9, 4]
insertion_sort(numList)
print(numList) # [-9, 1, 4, 7, 8, 13]
Python Implementation (Descending Order)
def insertion_sort_desc(list1):
for i in range(1, len(list1)):
key = list1[i]
j = i - 1
while j >= 0 and list1[j] < key: # flip: < instead of >
list1[j + 1] = list1[j]
j -= 1
list1[j + 1] = key
numList = [8, 7, 13, 1, -9, 4]
insertion_sort_desc(numList)
print(numList) # [13, 8, 7, 4, 1, -9]
Step-by-Step Dry Run Trace: Insertion Sort
Input: [8, 7, 13, 1, -9, 4] (n = 6)
Pass 1 (i=1): key = 7, sorted portion = [8]
| j | list[j] | list[j] > key? | Action |
|---|---|---|---|
| 0 | 8 | 8 > 7? Yes | Shift 8 right: [8, 8, 13, 1, -9, 4] |
| -1 | - | Exit loop | Place key at j+1=0 |
List: [7, 8, 13, 1, -9, 4] (sorted portion: [7, 8])
Pass 2 (i=2): key = 13, sorted portion = [7, 8]
| j | list[j] | list[j] > key? | Action |
|---|---|---|---|
| 1 | 8 | 8 > 13? No | Exit loop |
List: [7, 8, 13, 1, -9, 4] (13 already in place, sorted portion: [7, 8, 13])
Pass 3 (i=3): key = 1, sorted portion = [7, 8, 13]
| j | list[j] | list[j] > key? | Action |
|---|---|---|---|
| 2 | 13 | 13 > 1? Yes | Shift: [7, 8, 13, 13, -9, 4] |
| 1 | 8 | 8 > 1? Yes | Shift: [7, 8, 8, 13, -9, 4] |
| 0 | 7 | 7 > 1? Yes | Shift: [7, 7, 8, 13, -9, 4] |
| -1 | - | Exit loop | Place key at j+1=0 |
List: [1, 7, 8, 13, -9, 4] (sorted portion: [1, 7, 8, 13])
Pass 4 (i=4): key = -9, sorted portion = [1, 7, 8, 13]
| j | list[j] | list[j] > key? | Action |
|---|---|---|---|
| 3 | 13 | Yes | Shift: [1, 7, 8, 13, 13, 4] |
| 2 | 8 | Yes | Shift: [1, 7, 8, 8, 13, 4] |
| 1 | 7 | Yes | Shift: [1, 7, 7, 8, 13, 4] |
| 0 | 1 | Yes | Shift: [1, 1, 7, 8, 13, 4] |
| -1 | - | Exit loop | Place key at j+1=0 |
List: [-9, 1, 7, 8, 13, 4] (sorted portion: [-9, 1, 7, 8, 13])
Pass 5 (i=5): key = 4, sorted portion = [-9, 1, 7, 8, 13]
| j | list[j] | list[j] > key? | Action |
|---|---|---|---|
| 4 | 13 | Yes | Shift: [-9, 1, 7, 8, 13, 13] |
| 3 | 8 | Yes | Shift: [-9, 1, 7, 8, 8, 13] |
| 2 | 7 | Yes | Shift: [-9, 1, 7, 7, 8, 13] |
| 1 | 1 | 1 > 4? No | Exit loop |
Place key at j+1=2: [-9, 1, 4, 7, 8, 13]
Final sorted list: [-9, 1, 4, 7, 8, 13]
Time Complexity
- Worst case: O(n^2), list is reverse sorted (maximum shifts)
- Best case: O(n), list is already sorted (inner loop never executes)
- Good for: nearly sorted data and small datasets
Sorting Strings
All three algorithms work with strings too (Python compares strings lexicographically):
def bubble_sort(lst):
n = len(lst)
for i in range(n):
for j in range(0, n - i - 1):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]
names = ["Priya", "Aarav", "Zara", "Kabir", "Meera"]
bubble_sort(names)
print(names) # ['Aarav', 'Kabir', 'Meera', 'Priya', 'Zara']
Note on string comparison: Python compares strings character by character using ASCII/Unicode values.
- 'A' (65) < 'Z' (90) < 'a' (97) < 'z' (122), So "Zara" < "aarav" (uppercase comes before lowercase), To sort case-insensitively, use
.lower()in comparison
Sorting with Custom Key (Bonus)
Sort a list of tuples by second element (marks):
students = [("Aarav", 85), ("Priya", 92), ("Kabir", 78), ("Zara", 95)]
# Using insertion sort
def sort_by_marks(lst):
for i in range(1, len(lst)):
key = lst[i]
j = i - 1
while j >= 0 and lst[j][1] > key[1]: # compare by marks (index 1)
lst[j + 1] = lst[j]
j -= 1
lst[j + 1] = key
sort_by_marks(students)
print(students)
# [('Kabir', 78), ('Aarav', 85), ('Priya', 92), ('Zara', 95)]
Comparison of Sorting Algorithms
| Feature | Bubble Sort | Selection Sort | Insertion Sort |
|---|---|---|---|
| Approach | Compare adjacent, swap | Find min, swap to front | Insert in correct position |
| Best case | O(n) with optimization | O(n^2) | O(n) |
| Worst case | O(n^2) | O(n^2) | O(n^2) |
| Average case | O(n^2) | O(n^2) | O(n^2) |
| Stable? | Yes | No | Yes |
| In-place? | Yes | Yes | Yes |
| Best for | Nearly sorted data | When swaps are expensive | Nearly sorted, small data |
| # Swaps (worst) | O(n^2) | O(n), exactly n-1 | O(n^2) shifts |
| # Comparisons (worst) | O(n^2) | O(n^2) | O(n^2) |
What is stability? A sorting algorithm is stable if it preserves the relative order of equal elements. Example:
Input: [(A, 3), (B, 1), (C, 3), (D, 2)]
Stable sort by number: [(B, 1), (D, 2), (A, 3), (C, 3)] <- A before C (original order)
Unstable sort: [(B, 1), (D, 2), (C, 3), (A, 3)] <- C before A (order changed)
Selection sort is unstable because it can swap non-adjacent elements.
Summary Table: Number of Operations
For the example [8, 7, 13, 1, -9, 4]:
| Algorithm | Passes | Comparisons | Swaps/Shifts |
|---|---|---|---|
| Bubble Sort | 5 | 15 | 11 swaps |
| Selection Sort | 5 | 15 | 3 swaps |
| Insertion Sort | 5 | 12 | 12 shifts |
Practice Problems
- Dry run bubble sort on [5, 3, 8, 1, 2]. Show the list after each pass.
- Dry run selection sort on [29, 10, 14, 37, 13]. Show min_idx and swap at each step.
- Dry run insertion sort on [12, 11, 13, 5, 6]. Show key and shifts at each step.
- Write a Python function to sort a list of strings in descending order using selection sort.
- How many passes are needed to sort [1, 2, 3, 4, 5] using optimized bubble sort? (Answer: 1)
- Which sorting algorithm makes the fewest swaps? (Answer: Selection sort)
- Can you modify bubble sort to detect if the list is already sorted? Write the code.
- Sort the list [64, 25, 12, 22, 11] using all three algorithms. Compare the number of swaps.
Key Points Students Miss
- Bubble sort, after pass i, the last i elements are in their final position
- Selection sort, minimum swaps (exactly n-1), but always O(n^2) comparisons
- Insertion sort, starts from index 1, not 0 (first element is "sorted" by itself)
- For descending order: just flip the comparison (
>becomes<) - All three are O(n^2) in worst case, inefficient for large datasets
- Python's built-in
sort()uses TimSort which is O(n log n), much faster - Selection sort is the only unstable sort among the three
- Insertion sort uses shifts (not swaps), element slides into position
- Optimized bubble sort has O(n) best case; standard bubble sort does not
- In exam dry-run questions, show the list state after EACH pass - not just the final result
Board Exam Tips
- For dry-run questions, create a clear table with columns for each variable, show values at EVERY step
- Bubble sort is the most commonly asked, know it thoroughly with trace
- When asked to "write a program to sort", you can use any of the three algorithms unless specified
- For "difference between" questions, compare at least 4 points (approach, complexity, swaps, stability)
- Remember: changing
>to<in the comparison converts ascending sort to descending sort - If asked about efficiency, mention both time complexity AND space complexity (all three are O(1) extra space)
Prefer watching over reading?
Subscribe for free.