AxoPrep

Computer Science

B2.4 Programming algorithms

Measuring efficiency with Big O, searching with linear and binary search, sorting with bubble and selection sort, and (HL) recursion and quicksort. Study guide, 7 practice questions with markschemes and 4 flashcards. Free.

Big O notation and efficiency · linear and binary search · bubble and selection sort · (HL) recursion · (HL) quicksort and other recursive algorithms

Practise all 7 questions Study 4 flashcards

Study guide

Big O notation

Big O describes how the number of steps grows as the input size \(n\) grows, in the worst case, ignoring constants.

Big ONameExample
\(O(1)\)constantaccess a[i]
\(O(\log n)\)logarithmicbinary search
\(O(n)\)linearlinear search, one loop
\(O(n \log n)\)linearithmicquicksort (average), merge sort
\(O(n^2)\)quadraticbubble and selection sort, nested loops

Doubling \(n\) doubles an \(O(n)\) algorithm's work but quadruples an \(O(n^2)\) one, and adds just one step to \(O(\log n)\).

Linear search

Check each item in turn. Works on unsorted data; worst case \(n\) comparisons.

def linear_search(items, target):
    for i in range(len(items)):
        if items[i] == target:
            return i
    return -1

Binary search

Only on sorted data. Compare with the middle item and discard the half that cannot contain the target.

def binary_search(items, target):
    low, high = 0, len(items) - 1
    while low <= high:
        mid = (low + high) // 2
        if items[mid] == target:
            return mid
        elif items[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

Trace on [2, 5, 9, 14, 20, 27, 33, 41]:

Targetlowhighmiditems[mid]Action
270731414 < 27, low = 4
2747527found at index 5
60731414 > 6, high = 2
602155 < 6, low = 2
622299 > 6, high = 1
621--low > high, return -1

Each step halves the range, so a million items need at most 20 comparisons (\(2^{20} \approx 10^6\)). The cost is that the data must be kept sorted.

Bubble sort

Compare neighbours and swap if out of order; after each pass the largest remaining value has "bubbled" to the end. [5, 2, 8, 1, 4] ascending:

PassList after passSwaps
12, 5, 1, 4, 83
22, 1, 4, 5, 82
31, 2, 4, 5, 81
41, 2, 4, 5, 80 -> stop
def bubble_sort(a):
    n = len(a)
    for p in range(n - 1):
        swapped = False
        for i in range(n - 1 - p):
            if a[i] > a[i + 1]:
                a[i], a[i + 1] = a[i + 1], a[i]
                swapped = True
        if not swapped:
            break

The swapped flag ends early on sorted data (best case \(O(n)\)).

Selection sort

Find the smallest item in the unsorted part and swap it into the next position. [6, 3, 9, 2, 7]:

PassMinimum foundList after pass
122, 3, 9, 6, 7
23 (already in place)2, 3, 9, 6, 7
362, 3, 6, 9, 7
472, 3, 6, 7, 9

Always \(O(n^2)\) comparisons but at most \(n - 1\) swaps, fewer than bubble sort, which helps when writing to memory is expensive.

Recursion (HL)

A recursive function calls itself on a smaller version of the problem. It needs a base case that stops the calls and a recursive case that moves towards it.

def power(b, e):
    if e == 0:            # base case
        return 1
    return b * power(b, e - 1)

power(3, 3) -> 3 x power(3, 2) -> 3 x 3 x power(3, 1) -> 3 x 3 x 3 x power(3, 0) = 27. That is 4 calls. Each call waits on the call stack; without a base case the stack fills and the program crashes (stack overflow). Recursion gives short, elegant code for naturally self-similar problems (trees, divide and conquer) but uses more memory than a loop.

Quicksort (HL)

Choose a pivot, partition the rest into items smaller and larger than it, then quicksort each part.

def quicksort(a):
    if len(a) <= 1:
        return a
    pivot = a[0]
    smaller = [x for x in a[1:] if x < pivot]
    larger = [x for x in a[1:] if x >= pivot]
    return quicksort(smaller) + [pivot] + quicksort(larger)

[4, 7, 1, 6, 2]: pivot 4 -> [1, 2] + 4 + [7, 6]; then [1, 2] -> [] + 1 + [2]; [7, 6] -> [6] + 7 + []. Result [1, 2, 4, 6, 7]. Average \(O(n \log n)\); worst case \(O(n^2)\) when the pivot is always the smallest or largest (for example, first-item pivot on already sorted data).

Common mistakes

Exam tips

Cheat sheet

AlgorithmBestAverageWorstNeeds sorted?
Linear searchO(1)O(n)O(n)No
Binary searchO(1)O(log n)O(log n)Yes
Bubble sort (with flag)O(n)O(n^2)O(n^2)-
Selection sortO(n^2)O(n^2)O(n^2)-
Quicksort (HL)O(n log n)O(n log n)O(n^2)-

Key definitions

Big O notation

A way of describing how an algorithm's time or space requirement grows with input size, focusing on the dominant term in the worst case.

Linear search

A search that examines each item in order until the target is found or the list ends.

Binary search

A search on sorted data that repeatedly compares with the middle item and discards half of the remaining range.

Bubble sort

A sort that repeatedly compares adjacent items and swaps them if they are out of order.

Selection sort

A sort that repeatedly selects the smallest remaining item and places it at the next position.

Recursion (HL)

A technique in which a function solves a problem by calling itself on smaller instances.

Base case (HL)

The condition in a recursive function that returns a result without making a further recursive call.

Pivot (HL)

The item chosen in quicksort around which the other items are partitioned.

Stack overflow (HL)

An error that occurs when too many nested calls exhaust the memory reserved for the call stack.

Practice questions

SLPaper 2Standard

Question 1

(a) [4]

Construct a Python function find_first(names, target) that performs a linear search and returns the index of the first occurrence of target in the list names, or −1 if it is not present.

Markscheme
  • 1 Loops through each index of the list
  • 1 Compares the element at that index with target
  • 1 Returns the index as soon as a match is found
  • 1 Returns −1 only after the whole list has been checked
(b) [1]

State the maximum number of comparisons needed for a list of n items.

Markscheme
  • 1 n
SLPaper 2Challenging

Question 2

(a) [3]

A binary search (using mid = (low + high) // 2) is performed on the sorted list [3, 8, 12, 19, 23, 31, 40, 47, 52] for the value 40. Trace the values of low, high and mid, and state how many items are compared.

Markscheme
  • 1 low = 0, high = 8, mid = 4: 23 < 40 so low = 5
  • 1 low = 5, high = 8, mid = 6: 40 found
  • 1 2 comparisons
(b) [3]

Trace the same search for the value 10 and state how the algorithm detects that it is not present.

Markscheme
  • 1 mid = 4 (23 > 10) so high = 3
  • 1 mid = 1 (8 < 10) so low = 2; mid = 2 (12 > 10) so high = 1
  • 1 Stops because low (2) > high (1): value not present
(c) [1]

State one requirement of binary search that linear search does not have.

Markscheme
  • 1 The list must be sorted
SLPaper 2Standard

Question 3

(a) [3]

Show the list [7, 3, 9, 1, 5] after each pass of an ascending bubble sort.

Markscheme
  • 1 After pass 1: [3, 7, 1, 5, 9]
  • 1 After pass 2: [3, 1, 5, 7, 9]
  • 1 After pass 3: [1, 3, 5, 7, 9]
(b) [1]

State the total number of swaps made.

Markscheme
  • 1 6 (3 + 2 + 1)
(c) [2]

Explain how a flag can make bubble sort stop early.

Markscheme
  • 1 A Boolean is set to False at the start of each pass and set to True when a swap happens
  • 1 If a whole pass finishes with no swaps, the list is sorted and the loop stops

Key facts

Linear vs binary search complexity

Linear: O(n), works on unsorted data. Binary: O(log n), needs sorted data.

Bubble sort in one line

Repeatedly compare adjacent pairs and swap if out of order; after each pass the largest remaining item is in place. O(n²).

Selection sort in one line

Find the smallest item in the unsorted part and swap it into the next position; at most n − 1 swaps. O(n²).

(HL) Two parts every recursive function needs

A base case that stops the recursion, and a recursive case that moves towards the base case.