Grade by Grade
A. Programming Concepts and Skills · Arrays and Subprograms

Sorting Algorithms: Selection Sort and Bubble Sort

A2.3 A1.6 A3.2 A4.4About 75 minutes
Learning goals
  • I can swap two elements of a list safely.
  • I can explain, trace and write selection sort and bubble sort using nested loops.
  • I can count the comparisons a sort makes and explain why sorting a long list takes much longer.
  • I can use Python's sorted() and list.sort() and explain how they differ.

Words to know

sort
To arrange the elements of a list in order, e.g. smallest to largest (ascending) or largest to smallest (descending).
swap
To exchange the values of two elements.
pass
One complete run of a sort's inner loop through the unsorted part of the list.
selection sort
A sort that repeatedly selects the smallest remaining element and swaps it into the next position.
bubble sort
A sort that repeatedly compares neighbouring elements and swaps them if they are out of order, so large values 'bubble' to the end.
nested loop
A loop inside another loop. The inner loop runs completely for every pass of the outer loop.

Why sort?

A hockey league's standings, a class list in alphabetical order, the top 10 scores in a game: all sorted lists. Sorted data is easier for people to read, and it lets programs find things faster (you will add and remove elements in a sorted list in Inserting, Deleting and Parallel Lists). Python can sort with one call, but every computer science student learns how sorting actually works, because sorting algorithms are the classic example of nested loops working on an array. This lesson builds two of them.

Step 1: swapping two elements

To swap a[0] and a[1], you cannot just write a[0] = a[1] then a[1] = a[0]: the first line destroys the old a[0]. Save it in a temporary variable first. Python also has a one-line swap that does the same thing.

a = [5, 9]
temp = a[0]
a[0] = a[1]
a[1] = temp
print(a)
a[0], a[1] = a[1], a[0] # Python's one-line swap
print(a)

Output: [9, 5] then [5, 9] (swapped back).

Selection sort

Idea: find the smallest element and put it first. Then find the smallest of the rest and put it second. Keep going.

  1. For each position start from 0 to n − 2:
  2. Find the index of the smallest element from start to the end (the minimum algorithm from Highest, Lowest and Linear Search).
  3. Swap it into position start. Now positions 0 to start are sorted and never change again.

The last element needs no pass of its own: when everything else is in place, it must be the largest.

2910143713start1029143713pass 1: min 10, swap with index 01013143729pass 2: min 13, swap with index 11013143729pass 3: min 14 already in place1013142937pass 4: min 29, swap with index 3
Selection sort on [29, 10, 14, 37, 13]. After each pass, one more element on the left (shaded) is in its final place. Pass 3 swaps 14 with itself. Five elements need only 4 passes: once 4 are in place, the last one is too.
def selection_sort(values):
"""Sort values into increasing order, in place."""
n = len(values)
for start in range(n - 1):
smallest = start
for i in range(start + 1, n):
if values[i] < values[smallest]:
smallest = i
values[start], values[smallest] = values[smallest], values[start]
data = [29, 10, 14, 37, 13]
selection_sort(data)
print(data)

Output: [10, 13, 14, 29, 37]. The outer loop chooses the position to fill; the inner loop searches the unsorted part.

Bubble sort

Idea: walk along the list comparing neighbours. Whenever a pair is in the wrong order, swap it. After one pass the largest element has "bubbled" to the end. Repeat on the shorter unsorted part.

A useful improvement: if a whole pass makes no swaps, the list is already sorted, so stop early.

83618 > 3: swap38618 > 6: swap36818 > 1: swap3618end of pass 1: 8 is in place
The first pass of bubble sort on [8, 3, 6, 1]. Each row compares one pair of neighbours (shaded yellow) and swaps if the left one is bigger. After the pass, the largest value, 8, is at the end (shaded green).
def bubble_sort(values):
"""Sort values into increasing order, in place."""
n = len(values)
for p in range(n - 1):
swapped = False
for i in range(n - 1 - p):
if values[i] > values[i + 1]:
values[i], values[i + 1] = values[i + 1], values[i]
swapped = True
if not swapped:
break # no swaps: already sorted
times = [8, 3, 6, 1]
bubble_sort(times)
print(times)

Output: [1, 3, 6, 8]. The inner loop stops at n - 1 - p because the last p elements are already in place, and i + 1 must stay in bounds.

Worked example

Example 1: Trace bubble sort completely

Trace bubble sort on [8, 3, 6, 1]. Show the list after each pass and count the comparisons.

  1. Pass 1 (i = 0, 1, 2; 3 comparisons): swap 8/3, swap 8/6, swap 8/1 → [3, 6, 1, 8].
  2. Pass 2 (i = 0, 1; 2 comparisons): 3 < 6 no swap; 6 > 1 swap → [3, 1, 6, 8].
  3. Pass 3 (i = 0; 1 comparison): 3 > 1 swap → [1, 3, 6, 8].
  4. Total comparisons: 3 + 2 + 1 = 6. For n elements the worst case is (n − 1) + (n − 2) + ... + 1 = n(n − 1)/2. Selection sort always makes this many comparisons.

Passes give [3, 6, 1, 8], [3, 1, 6, 8], [1, 3, 6, 8]; 6 comparisons.

How long does sorting take?

n(n − 1)/2 grows quickly. For 10 elements it is 45 comparisons; for 1000 it is 499 500; for 1 000 000 it is about 500 billion. Doubling the list roughly quadruples the work. That is why these simple sorts are fine for class lists but not for huge data. Python's built-in sort uses a much faster algorithm (called Timsort), and Grade 12 computer science (ICS4U) goes on to faster sorting methods.

n (elements)comparisons n(n − 1)/2
510
1045
1004 950
1 000499 500
Comparisons made by selection sort (and by bubble sort in the worst case).

Built-in sorting

  • sorted(a) returns a new sorted list and leaves a unchanged.
  • a.sort() sorts in place and returns None.
  • Both accept reverse=True for descending order. Strings sort alphabetically (by character code, so all capital letters come before lower-case ones).
towns = ["Sudbury", "Barrie", "Kenora", "Ajax"]
print(sorted(towns))
print(towns)
towns.sort(reverse=True)
print(towns)
print(sorted([3, 1, 2]) == [1, 2, 3])

Output: ['Ajax', 'Barrie', 'Kenora', 'Sudbury'], then the unchanged original ['Sudbury', 'Barrie', 'Kenora', 'Ajax'], then ['Sudbury', 'Kenora', 'Barrie', 'Ajax'], then True.

Worked example

Example 2: Sorting in descending order

Change selection_sort so it puts the largest first (descending order), e.g. for a top-scores list.

  1. Only the comparison changes: the inner loop should look for the largest remaining element.
  2. Replace if values[i] < values[smallest]: with if values[i] > values[largest]: (renaming smallest to largest so the code still reads correctly).
  3. Test: [29, 10, 14, 37, 13] should become [37, 29, 14, 13, 10].

One comparison flips the order; good variable names keep the code honest.

Just for fun

Some sorting algorithms really have silly names: "bogosort" shuffles the list randomly and checks whether it happens to be sorted, over and over. For a deck of 52 cards, you would expect to wait far longer than the age of the universe.

Hands-on activity

Sort a hand of cards

You need: a deck of playing cards (use only the cards Ace to 10 of one suit, Ace = 1), a table, and a partner.

  1. Shuffle the 10 cards and deal them face up in a row.
  2. Selection sort: scan the row for the smallest card and swap it with the first card. Then scan from the second card, and so on. Your partner counts every comparison (each time you compare two cards).
  3. Shuffle and deal again. Bubble sort: compare cards 1 and 2, swap if needed, then 2 and 3, and so on to the end. Repeat passes until a pass has no swaps. Count comparisons again.
  4. Compare your counts with n(n − 1)/2 = 45. Did bubble sort stop early?
  5. Code both sorts in Python and add a counter variable to check your counts.

What to look for: the student explains why the left part stays sorted in selection sort, and why the right part stays sorted in bubble sort.

Your turn

1

Why does a[0] = a[1] then a[1] = a[0] fail to swap?

2

Show the list after each pass of selection sort on [5, 2, 8, 1].

3

Show the list after the first pass of bubble sort on [4, 1, 3, 2].

4

How many comparisons does selection sort make on a list of 6 elements?

5

What is the difference between sorted(a) and a.sort()?

6

In bubble sort, why does the inner loop use range(n - 1 - p) and not range(n)?

More practice: warm-up

1

What does "ascending order" mean?

2

Which sort compares neighbouring elements?

3

Which sort selects the smallest remaining element each pass?

4

What does sorted([3, 1, 2], reverse=True) give?

5

What does print([2, 1].sort()) show?

More practice: on your own

1

Trace bubble sort with early exit on [1, 2, 4, 3]. How many passes run?

2

Add a comparison counter to selection_sort and return it. What does it return for a 5-element list?

3

Predict the output: print(sorted(["pear", "Apple", "banana"]))

4

A list of 2000 names takes 2 s to sort with selection sort on an old computer. Estimate the time for 4000 names.

5

Write a function is_sorted(values) that returns True if the list is in ascending order.

Challenge

1

A team stores names = ["Ana", "Raj", "Kim"] and parallel times = [52.4, 49.8, 55.1]. Write a sort that orders the skaters by time and keeps names matched to their times.

2

Explain why bubble sort with early exit needs only n − 1 comparisons on a list that is already sorted, but selection sort still needs n(n − 1)/2.

3

Write insertion_sort(values): take each element from index 1 on and shift it left past every larger element. Trace it on [3, 1, 2].

Check your understanding

Quiz

1.

After pass 1 of selection sort on [7, 2, 9, 4], what is the list?

2.

After pass 1 of bubble sort on [7, 2, 9, 4], what is the list?

3.

How many comparisons does selection sort make on 8 elements?

4.

Which line correctly swaps a[i] and a[j] in Python?

5.

What does b = sorted(a) do to a?

6.

If a list gets 10 times longer, about how much more work does selection sort do?

For parents and teachers

Big idea: Sorting is the classic nested-structure array algorithm (A2.3), and it uses everything about accessing and modifying arrays (A1.6). Writing the sorts as functions practises A3.2, and tracing passes by hand is A4.4.

How to help: The card activity is the best way to understand sorting. Ask your teen to sort cards out loud, naming each comparison and swap. Then ask, "What part of the row is already sorted?"

Common difficulties:

  • Losing a value when swapping without a temporary variable.
  • Off-by-one bounds in the inner loop (IndexError on values[i + 1]).
  • Mixing up the two algorithms: selection sort finds the minimum then swaps once per pass; bubble sort swaps neighbours many times per pass.
  • Writing a = a.sort(), which sets a to None.