Skip to content

Computational thinking and Problem-solving

A-Level Computer Science · Topic 19

Train
19.1

Searching algorithms

Syllabus
Candidates should be able to: Notes and guidance
Show understanding of linear search and binary search methods Write an algorithm to implement a linear search Write an algorithm to implement a binary search The conditions necessary for the use of a binary search How the performance of a binary search varies according to the number of data items
Show understanding of insertion sort and bubble sort methods Write an algorithm to implement an insertion sort Write an algorithm to implement a bubble sort Performance of a sorting routine may depend on the initial order of the data and the number of data items
Show understanding of and use Abstract Data Types (ADT) Write algorithms to find an item in each of the following: linked list, binary tree Write algorithms to insert an item into each of the following: stack, queue, linked list, binary tree Write algorithms to delete an item from each of the following: stack, queue, linked list Show understanding that a graph is an example of an ADT. Describe the key features of a graph and justify its use for a given situation. Candidates will not be required to write code for a graph structure
Show how it is possible for ADTs to be implemented from another ADT Describe the following ADTs and demonstrate how they can be implemented from appropriate built-in types or other ADTs: stack, queue, linked list, dictionary, binary tree
Show understanding that different algorithms which perform the same task can be compared by using criteria (e.g. time taken to complete the task and memory used) Including use of Big O notation to specify time and space complexity

Source: Cambridge International syllabus

Big O: how algorithms scale
Insertion sort: slide each card into place
Bubble sort, pass by pass
Binary search: halve and conquer

A search finds a target value in a collection (often an array 数组) and returns its position, or "not found".

An open telephone directory Searching a sorted list, like a phone book, is far faster than checking every entry one by one

Linear search

A linear search 线性查找 walks from start to end, comparing each element with the target:

FOR i ← 1 TO n
    IF A[i] = target THEN RETURN i
NEXT i
RETURN -1   // not found

No preparation is needed, so it works on any list. Worst case O($n$) (target at the end or absent); best case 1 comparison. Use it on unsorted data or small lists. (The returned -1 is a sentinel value — an impossible position that means "not found"; the caller tests IF result = -1.)

A row of alphabet cells A to Z; cells A to V are shaded as checked and W is highlighted as the match, with a pointer below W Linear search checks every letter in turn — 23 comparisons to find W

Binary search

A binary search 二分查找 needs the data sorted. Look at the middle element; if it is the target, done; if the target is smaller, search the left half, else the right half — halving the range each time:

low ← 1
high ← n
WHILE low <= high DO
    mid ← (low + high) DIV 2
    IF A[mid] = target THEN RETURN mid
    IF A[mid] < target THEN
        low ← mid + 1
    ELSE
        high ← mid - 1
    ENDIF
ENDWHILE
RETURN -1

Worst case O($\log_{2} n$) — for a million items, about 20 comparisons. Much faster than linear search on large sorted arrays, but you must sort first (a one-off O($n \log n$) cost), worth it if you search many times.

Three rows showing binary search on the sorted alphabet; the active low-to-high range halves each step as the middle letter M, then T, then W is compared with W Binary search halves the range each step (low / mid / high) — just 3 comparisons to find W

Explore

Linear vs binary search

Search for a value. Binary search halves the list each step (only on sorted data); linear search checks one by one.

Vocabulary Train
English Chinese Pinyin
array 数组 shù zǔ
linear search 线性查找 xiàn xìng chá zhǎo
binary search 二分查找 èr fēn chá zhǎo
19.1

Sorting algorithms

Bubble sort

A bubble sort 冒泡排序 repeatedly walks the array, swapping adjacent pairs that are out of order, so the largest "bubbles" to the end each pass:

FOR pass ← 1 TO n - 1
    swapped ← FALSE
    FOR i ← 1 TO n - pass
        IF A[i] > A[i + 1] THEN
            temp ← A[i]
            A[i] ← A[i + 1]
            A[i + 1] ← temp
            swapped ← TRUE
        ENDIF
    NEXT i
    IF swapped = FALSE THEN EXIT FOR   // already sorted
NEXT pass

Best case O($n$) (already sorted, with the early exit); average/worst O($n^{2}$). Simple but slow for large $n$.

Insertion sort

An insertion sort 插入排序 builds a sorted prefix from the left, inserting each new element into place by shifting larger ones right:

FOR i ← 2 TO n
    key ← A[i]
    j ← i - 1
    WHILE j >= 1 AND A[j] > key DO
        A[j + 1] ← A[j]
        j ← j - 1
    ENDWHILE
    A[j + 1] ← key
NEXT i

Best case O($n$) (already sorted); worst O($n^{2}$). Good for small or nearly-sorted arrays. It sorts in place 原地 and is stable 稳定 (keeps the order of equal elements).

Tracing a sort

A common task is to show the array after each outer pass. For [D, T, H, R] with insertion sort: pass 1 (key T) no change; pass 2 (key H) → [D, H, T, R]; pass 3 (key R) → [D, H, R, T].

Rows tracing an insertion sort of D, T, H, R across three passes; the sorted prefix is shaded and arrows show each larger element shifting right to let the key drop in An insertion sort of [D, T, H, R], shifting each key into its place pass by pass

Explore

Watch a sort run

Step through a sort and watch the bars settle into order — how a sorting algorithm works pass by pass.

Vocabulary Train
English Chinese Pinyin
bubble sort 冒泡排序 mào pào pái xù
insertion sort 插入排序 chā rù pái xù
in place 原地 yuán dì
stable 稳定 wěn dìng
19.1

ADTs in algorithms

The Abstract Data Types (ADTs) from Topic 10 appear inside many algorithms: a stack drives depth-first traversal and undo; a queue 队列 drives breadth-first traversal and print ordering; a linked list 链表 lets data grow and shrink.

ADTs can be built from other ADTs, not just from arrays: a queue from two stacks; a stack from a linked list (push = prepend a head node 节点); a queue from a linked list with head and tail pointers 指针; a binary tree 二叉树 from nodes with two child pointers; a dictionary 字典 stores key→value pairs (often on a hash table). Layering this way separates concerns — the algorithm using the ADT need not know how it is built.

A binary tree with root 27, a left subtree of 19, 16, 21 and 17, and a right subtree of 36, 42, 89 and 55, with the root, the left and right pointers, and a leaf node labelled A binary tree: each node has up to two child nodes

A binary search tree with root 4 (left subtree 2 over 1 and 3, right subtree 6 over 5 and 7); pre-order visits 4 2 1 3 6 5 7, in-order 1 2 3 4 5 6 7 (sorted), post-order 1 3 2 5 7 6 4 Three depth-first traversals of a binary tree: pre-order, in-order (sorted order) and post-order

Vocabulary Train
English Chinese Pinyin
stack zhàn
queue 队列 duì liè
linked list 链表 liàn biǎo
node 节点 jié diǎn
pointers 指针 zhǐ zhēn
binary tree 二叉树 èr chā shù
dictionary 字典 zì diǎn
19.1

Comparing algorithms

Time complexity

Time complexity 时间复杂度 is how the running time grows with input size $n$, written in Big-O notationO表示法 (the dominant term): O(1) constant, O($\log n$) binary search, O($n$) linear search, O($n \log n$) good sorts, O($n^{2}$) bubble/insertion sort. A smaller order is better at scale, even if another algorithm is faster for small $n$.

To make that concrete: to sort a million items, an $O(n \log n)$ sort finishes in a fraction of a second, while an $O(n^{2})$ sort can take minutes.

Worked example. A sorted list holds $1000$ items. How many comparisons does each search need in the worst case?

A linear search checks items one at a time, so it may need up to $1000$ comparisons — this is $O(n)$. A binary search halves the list each step, so it needs at most $\lceil \log_2 1000 \rceil = 10$ comparisons — this is $O(\log n)$. Doubling the list to $2000$ items adds only one comparison to the binary search, but up to another $1000$ to the linear search — which is why the order of growth, not raw speed, decides the winner at scale.

A graph of running time against input size n for the common orders: O(1) and O(log n) stay almost flat, O(n) rises gently, O(n log n) more steeply, and O(n squared) climbs away fastest How the common orders of growth compare: a smaller order wins at scale

A line graph of running time against the number of elements n: bubble sort and insertion sort rise steeply as O(n squared), while quick sort stays low as O(n log n) How sorting time grows with the number of elements $n$: $O(n^2)$ sorts climb away from an $O(n\log n)$ sort

Space complexity

Space complexity 空间复杂度 is the extra memory needed. Bubble and insertion sort use O(1) extra (in place); merge sort uses O($n$); recursion uses stack memory proportional to its depth. There is often a time–memory trade-off.

Other criteria

Simplicity (easier to code and maintain), stability, and adaptiveness (faster on nearly-sorted data). The right algorithm depends on the data and the constraints.

Explore

How running time grows with n

Slide n upward and compare the curves: O(1) and O(log n) stay almost flat, O(n) rises steadily, O(n²) explodes. This is why Big-O — not a stopwatch — is how we compare algorithms on large inputs.

Explore

Big-O growth

Change the input size n and compare how fast each algorithm's work grows — the idea behind time complexity.

Vocabulary Train
English Chinese Pinyin
time complexity 时间复杂度 shí jiān fù zá dù
Big-O notation 大O表示法 dà O biǎo shì fǎ
space complexity 空间复杂度 kōng jiān fù zá dù
19.2

Recursion

Syllabus
Candidates should be able to: Notes and guidance
Show understanding of recursion Essential features of recursion How recursion is expressed in a programming language Write and trace recursive algorithms When the use of recursion is beneficial
Show awareness of what a compiler has to do to translate recursive programming code Use of stacks and unwinding

Source: Cambridge International syllabus

Recursion: the call stack winds up and unwinds

Recursive algorithms use recursion 递归: the routine calls itself with a smaller version of the same problem, until a base case 基本情形 ends the chain. It has two parts: the base case (small enough to solve directly — without it the recursion never stops) and the recursive case 递归情形 (reduce the input and call itself).

Factorial 阶乘:

FUNCTION Factorial(n : INTEGER) RETURNS INTEGER
    IF n = 0 OR n = 1 THEN
        RETURN 1
    ELSE
        RETURN n * Factorial(n - 1)
    ENDIF
ENDFUNCTION

Recursion is natural for self-similar problems: trees, divide-and-conquer 分治 (binary search, merge sort), and nested data. When it is a poor fit, a loop is usually cleaner.

Tracing a recursive call

For Factorial(4): the calls go down to Factorial(1)=1, then unwinding multiplies back up: 2*1=2, 3*2=6, 4*6=24. Final result 24. Track each pending call on a stack.

The call stack for Factorial(4): each call pushes a frame down to the base case Factorial(1)=1, then the stack unwinds, returning 2 = 2 times 1, 6 = 3 times 2 and 24 = 4 times 6 Recursion uses the call stack: calls push frames down to the base case, then returns unwind back up

Risks

  • infinite recursion if the base case is missed — crashes with a stack overflow 栈溢出.
  • high memory use for deep recursion.
  • slow if it repeats work (naive Fibonacci is exponential — use a loop or memoisation 记忆化).
Explore

Recursion unwinds from the leaves up

Step through fib(4) in the order the calls actually finish: the leaves (base cases) resolve first, then each parent combines its children. Notice fib(2) is computed twice — that repeated work is why naive recursion is slow.

Vocabulary Train
English Chinese Pinyin
recursion 递归 dì guī
base case 基本情形 jī běn qíng xíng
recursive case 递归情形 dì guī qíng xíng
factorial 阶乘 jiē chéng
divide-and-conquer 分治 fēn zhì
stack overflow 栈溢出 zhàn yì chū
memoisation 记忆化 jì yì huà
19.2

What the compiler does for recursive code

Recursion needs each call to have its own copy of its parameters 参数 and local variables 局部变量. The compiler keeps these on the call stack 调用栈. For each call it pushes a stack frame 栈帧 holding the parameters, the local variables, and the return address 返回地址 (where to resume in the caller). When the function returns, the return value is handed back, the frame is popped, and control resumes at the return address.

Because each call has its own frame, recursive calls don't trample each other's variables. The stack can grow large for deep recursion, which is why very deep recursion may overflow it. This is the same call-and-return mechanism used for ordinary (non-recursive) calls — there is no special "recursion mechanism".

Vocabulary Train
English Chinese Pinyin
parameters 参数 cān shù
local variables 局部变量 jú bù biàn liàng
call stack 调用栈 diào yòng zhàn
stack frame 栈帧 zhàn zhēn
return address 返回地址 fǎn huí dì zhǐ
19.2

Exam tips

  • Match each algorithm to its Big-O: linear search $O(n)$, binary search $O(\log n)$, bubble/insertion $O(n^2)$, good sorts $O(n \log n)$.
  • Binary search needs a sorted list and halves the search space each step.
  • A recursive routine needs a base case and a call to itself; explain how deep recursion overflows the call stack.
  • Trace a sort or search with a table when asked, showing each pass.

Log in or create account

IGCSE & A-Level