Intermediate: core algorithms · Sorting and searching · topic 26 of 61 · 35 min
Simple sorts: bubble, selection and insertion
Explanation
Why learn sorts you will not use?
In real code you call sorted() and move on. But interviewers ask about simple sorts because they teach the words used for every sort (pass, swap, in place, stable), they are easy to trace by hand, and insertion sort is genuinely used inside fast library sorts for small pieces. Sorting means putting items in order, here non-decreasing order: each item is less than or equal to the next.
Bubble sort
Walk through the list and compare each pair of neighbours; if the left one is bigger, swap them. After one pass the largest item has "bubbled" to the end, so the next pass can stop one place earlier. With an early exit, if a whole pass makes no swaps, the list is already sorted and you stop.
[5, 1, 4, 2]
pass 1: swap 5,1 → [1, 5, 4, 2]; swap 5,4 → [1, 4, 5, 2]; swap 5,2 → [1, 4, 2, 5]
pass 2: 1,4 fine; swap 4,2 → [1, 2, 4, 5] (5 is already in place)
pass 3: 1,2 fine → no swaps, so stop earlySelection sort
Find the smallest item in the unsorted part and swap it to the front of that part; then the sorted part grows by one. It always does about n²/2 comparisons, even on sorted input, but at most n − 1 swaps, which matters only when writing to memory is very costly.
[29, 10, 14, 37, 13]
round 1: smallest is 10 → swap with 29 → [10, 29, 14, 37, 13]
round 2: smallest of the rest is 13 → swap with 29 → [10, 13, 14, 37, 29]
round 3: 14 is already in place
round 4: smallest of [37, 29] is 29 → swap → [10, 13, 14, 29, 37]Insertion sort
This is how most people sort a hand of playing cards. The left part is always sorted. Take the next item (the key), shift every bigger item in the sorted part one place right, and drop the key into the gap.
def insertion_sort(nums):
a = list(nums) # work on a copy
for i in range(1, len(a)):
key = a[i]
j = i - 1
while j >= 0 and a[j] > key: # > (not >=) keeps equal items in order
a[j + 1] = a[j] # shift right
j -= 1
a[j + 1] = key
return a
print(insertion_sort([5, 2, 4, 1])) # Output: [1, 2, 4, 5]Insertion sort does one shift per inversion (a pair in the wrong order, as in Module 5). A nearly sorted list has few inversions, so insertion sort runs in about O(n + inversions), close to O(n). That is why library sorts use it for short or almost-sorted runs.
Costs at a glance
| Sort | Best | Average and worst | Extra space | Stable? |
|---|---|---|---|---|
| Bubble (early exit) | O(n), already sorted | O(n²) | O(1) | Yes |
| Selection | O(n²) | O(n²) | O(1) | No |
| Insertion | O(n), already sorted | O(n²) | O(1) | Yes |
In place means the sort rearranges the list itself, using only O(1) extra memory. All three are in place (the code above copies the input first only so the caller's list does not change).
Stability
A sort is stable if items with EQUAL keys keep their original relative order. Suppose a class list is in alphabetical order and you sort it by marks. With a stable sort, students who scored the same marks stay in alphabetical order, so you get "sorted by marks, then by name" for free.
students = [("Asha", 82), ("Kiran", 75), ("Meena", 82), ("Ravi", 75)] # by name
print(sorted(students, key=lambda s: s[1], reverse=True))
# Output: [('Asha', 82), ('Meena', 82), ('Kiran', 75), ('Ravi', 75)]Selection sort is not stable because its long-distance swap can jump an item over an equal one. Sorting [(2, "a"), (2, "b"), (1, "c")] by the number, round 1 swaps (1, "c") with (2, "a"), giving [(1, "c"), (2, "b"), (2, "a")]: "b" is now before "a".
Common mistakes
- Using >= instead of > when comparing in bubble or insertion sort. It still sorts, but equal items get swapped, so the sort is no longer stable. Fix: move an item only when it is strictly bigger.
- Running bubble sort's inner loop to the end every pass. Fix: after pass k the last k items are final, so stop earlier.
- Forgetting the early exit, so a sorted list still costs O(n²). Fix: track whether a pass swapped anything.
- Off-by-one in insertion sort: losing the key by shifting before saving it. Fix: store key = a[i] first, and write it at a[j + 1] at the end.
- Calling these sorts in production code. Fix: use the built-in sort; learn these for interviews and for small or nearly sorted data.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Simple sorts: bubble, selection and insertion · 1/8
This lesson covers three simple sorts: bubble, selection and insertion. They are slow on big data, but they teach every word you need to talk about sorting.
Prefer a video by a teacher? Find free videos on “bubble sort selection sort insertion sort visualised stable sorting explained” on YouTube ↗
In real life: A leaderboard that is almost sorted
A quiz app for a coaching centre keeps its leaderboard sorted by score. When one student's score goes up, only that one entry is out of place. Re-sorting from scratch is wasteful; one step of insertion sort moves the entry left past the scores it now beats, which is O(n) at worst and often just a few moves. This is the "nearly sorted" case where insertion sort shines.
board = [("Neha", 95), ("Arjun", 90), ("Sara", 88), ("Vikram", 70)]
board[3] = ("Vikram", 92) # Vikram's score improved
i = 3
while i > 0 and board[i - 1][1] < board[i][1]:
board[i - 1], board[i] = board[i], board[i - 1]
i -= 1
print(board)Practice (0/3 done)
Write is_sorted(nums) that returns True if nums is in non-decreasing order (each item ≤ the next), else False. One pass, O(n) time, O(1) extra space; do not sort. Empty and one-item lists are sorted. Example: is_sorted([1, 2, 2, 5]) returns True.
Write insertion_sort(nums) that RETURNS A NEW sorted list (non-decreasing) and does not change nums. Write the algorithm yourself: do not call sorted() or .sort(). The tests can only check the results, so this is on your honour. Example: insertion_sort([5, 2, 4, 1]) returns [1, 2, 4, 5].
Write bubble_sort(nums) that sorts a COPY of nums with bubble sort and returns a tuple (sorted_list, swaps, passes). A pass walks the unsorted part from the left, swapping neighbours when the left one is STRICTLY bigger (equal items are never swapped). After each pass the unsorted part ends one place earlier. Stop as soon as a pass makes no swaps (that pass still counts), or when the unsorted part has fewer than 2 items. swaps is the total number of swaps; passes is the number of passes made. Do not call sorted() or .sort(). Example: bubble_sort([5, 1, 4, 2]) returns ([1, 2, 4, 5], 4, 3).
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Merge sort and quick sortSorting and searching · same module
- Sorting in real code: keys, comparators, counting sortSorting and searching · same module
- Binary searchSorting and searching · same module
- Recursion and sorting algorithmsPython Programming · Algorithms, data structures and testing
- 2D arrays and classic array algorithmsC Programming (from zero to systems) · Functions and arrays
- Sorting with keyPython Programming · Working with data
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does "stable sort" mean?
Answer: Items with equal keys keep their original relative order
Stability is about ties: equal items stay in the order they came in.
Which simple sort is NOT stable?
Answer: Selection sort
Selection sort's long-distance swap can jump an item over an equal one.
Insertion sort on a list that is already sorted takes…
Answer: O(n)
Each key is compared once with its left neighbour and nothing moves.
After the first pass of bubble sort on [4, 3, 1, 2], which item is guaranteed to be in its final place?
Answer: 4 at the end
Each pass carries the largest remaining item to the end of the unsorted part.
How many swaps does bubble sort make on [3, 1, 2]?
Answer: 2
Swaps equal inversions: (3, 1) and (3, 2), so 2.
What does it mean that Python's sort is stable?Related
Answer: Items that compare equal keep their original order
Stable sorting keeps equal items in the order they had before, which lets you sort by several keys in steps.
What is the time complexity of bubble sort in the worst case?Related
Quick sort always picks the last item as pivot. Which input is slowest?Related
Answer: A list that is already sorted
Each pivot is the largest item, so one side is empty every time: n levels, O(n²).
Which key sorts (name, marks) tuples by marks high to low, then name A to Z?Related
Answer: lambda s: (-s[1], s[0])
The tuple compares marks first (negated for high to low), then the name.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 4 more topics in “Sorting and searching” before its exam: Merge sort and quick sort, Sorting in real code: keys, comparators, counting sort, Binary search, Binary search on the answer.
- Module 6 exam: Sorting and searching · 14 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Trees and binary search trees. 61 topics left in the course before the final exam.