Fundamental Algorithms – Order of Growth

Learning Objectives & Overview

  • Understand why analysing algorithm performance matters when processing complex problems or large datasets.

  • Be able to:

    • Define and interpret “order of growth” for algorithms.

    • Express time-complexity using Big-O notation.

    • Recognise, derive and compare common growth-rate classes.

    • Analyse best-, average- and worst-case complexities for classic search/sort algorithms.

    • Apply complexity knowledge to algorithm choice, optimisation and exam questions.

The Need for Order of Growth

  • Even slow algorithms finish quickly on very small inputs; the real concern is performance as input size nn becomes large.

  • Running the same device with different algorithms can lead to markedly different resource consumption (time, memory).

  • Order-of-growth analysis allows:

    • Prediction of scalability.

    • Fair comparison of competing solutions on identical hardware.

    • Identification of bottlenecks before deployment.

Order of Growth & Complexity Measures

  • Rough measure of resources as nn \to \infty.

  • Two primary resources:

    • Time complexity – number of primitive operations / comparisons / steps.

    • Space complexity – memory required (excluded from the syllabus here).

  • We generally analyse worst-case unless otherwise stated; it provides an upper bound for all inputs.

Big-O Notation

  • Mathematical convention: O(f(n))O(f(n)) = the set of functions that grow no faster (within a constant factor) than f(n)f(n) for sufficiently large nn.

  • Ignore constants and lower-order terms; only the highest-order term dictates growth rate.

  • Example interpretations:

    • O(1)O(1) constant time (independent of nn).

    • O(n)O(n) linear growth (proportional to nn).

    • O(nlog2n)O(n\log_2 n) scaled logarithmic.

    • O(n2)O(n^2) quadratic.

    • O(2n)O(2^n) exponential.

Common Growth-Rate Functions

  • Ordered from slowest growth to fastest:

    • Constant O(1)O(1)

    • Logarithmic O(log2n)O(\log_2 n)

    • Linear O(n)O(n)

    • Linear-log O(nlog2n)O(n\log_2 n)

    • Quadratic O(n2)O(n^2)

    • Polynomial O(nk)O(n^k) (k>2)

    • Exponential O(2n)O(2^n)

Numerical Illustration (selected values)

  • Table excerpt (operations required vs nn):

    • n=10n=10: log<em>2n3.3\log<em>2 n \approx 3.3, nlog</em>2n33.2n\log</em>2 n \approx 33.2, n2=100n^2=100, 2n=10242^n=1024.

    • n=50n=50: log<em>2n5.6\log<em>2 n \approx 5.6, nlog</em>2n282.2n\log</em>2 n \approx 282.2, n2=2500n^2=2500, 2n1.13×10152^n \approx 1.13\times10^{15}.

  • Key takeaway: exponential functions explode rapidly; algorithms in that class are infeasible for large nn.

Worked Exercise – Counting Comparisons in a While Loop

word = input("Enter a word: ")
posn = 0
while posn < len(word):
    print(word[posn])
    posn += 1
  • Each iteration performs one comparison (posn < len(word)), plus one final false check.

  • For input of length nn: total comparisons =(n+1)=(n+1)O(n)O(n).

  • Specific cases:

    • "magic" (5 chars): 66 comparisons.

    • "hi there" (8 chars): 99 comparisons.

Example: is_prime Function

FOR i = 2 TO n
    IF n MOD i = 0 THEN RETURN FALSE
RETURN TRUE
  • Worst case: nn is prime ⇒ loop executes n2n-2 iterations.

  • If each iteration costs constant CC, total T=(n2)CT=(n-2)C.

  • Discard constants: T=O(n)T=O(n) (linear).

Linear Search Complexity

  • Best case: first element matches ⇒ 11 comparison ⇒ O(1)O(1).

  • Worst case: item at last position or absent ⇒ nn comparisons ⇒ O(n)O(n).

Binary Search Complexity

  • Works on sorted list by halving the search interval.

  • Best case: target equals middle element ⇒ O(1)O(1).

  • Worst case derivation:

    • Array length after kk halvings: n2k=1\frac{n}{2^k}=1k=log2nk=\log_2 n.

    • Comparisons per level: 11.

    • Total =log<em>2n=\log<em>2 nO(log</em>2n)O(\log</em>2 n).

  • Note: base-2 logarithm is required; log10\log_{10} differs.

Bubble Sort Complexity

  • Worst case (reverse order):

    • Passes: (N1)(N-1), each with (N1)(N-1) comparisons.

    • Total comparisons =(N1)2=N22N+1=(N-1)^2=N^2-2N+1O(N2)O(N^2).

  • Best case (already sorted):

    • Single pass, (N1)(N-1) comparisons ⇒ O(N)O(N).

Insertion Sort Complexity

  • Worst case (descending input when ascending desired):

    • Comparisons + swaps: 1+2++(N1)=N(N1)21+2+\dots+(N-1)=\frac{N(N-1)}{2}O(N2)O(N^2).

  • Best case (already sorted):

    • Only N1N-1 comparisons ⇒ O(N)O(N).

Quick Sort Complexity

  • Worst case (pivot always min or max):

    • Comparisons: n+(n1)++1=n(n+1)2n+(n-1)+\dots+1=\frac{n(n+1)}{2}O(n2)O(n^2).

  • Best / average case (pivot = median or chosen randomly):

    • Levels: log2n\log_2 n.

    • Comparisons per level: nn.

    • Total: nlog<em>2nn\log<em>2 nO(nlog</em>2n)O(n\log</em>2 n).

  • Pivot selection notes:

    • True median yields optimal split but costs linear time to find.

    • Random pivot greatly reduces probability of worst case; expected O(nlog2n)O(n\log_2 n).

    • For nearly-sorted data, insertion sort may outperform quicksort (best case O(n)O(n) vs O(nlogn)O(n\log n)).

Merge Sort Complexity

  • Divide list until size 11, then merge.

  • Recurrence: T(n)=2T(n2)+cnT(n)=2T\left(\frac{n}{2}\right)+cn.

  • Solving: T(n)=cnlog<em>2n+nT(n)=cn\log<em>2 n + nO(nlog</em>2n)O(n\log</em>2 n).

  • Characteristic features:

    • Same complexity for best, average, worst cases because splitting always proceeds.

    • Performance insensitive to initial order.

    • Not in-place: requires extra memory for merging.

Advantages & Disadvantages of Sorting Algorithms

  • Bubble Sort

    • + Simplicity, good for testing if list already sorted (best O(N)O(N)).

    • - Very slow on large/random data (O(N2)O(N^2)), rarely used in practice.

  • Insertion Sort

    • + Simple, efficient for very small or nearly-sorted datasets, stable.

    • - O(N2)O(N^2) worst case, beaten by more advanced algorithms on large data.

  • Quick Sort

    • + Fastest general-purpose in-place sort, low memory, average O(nlogn)O(n\log n).

    • - O(n2)O(n^2) worst case, unstable, not ideal for tiny arrays.

  • Merge Sort

    • + Guaranteed O(nlogn)O(n\log n), stable, optimal worst case.

    • - Needs extra space; copying overhead may hurt performance.

Choosing a Sorting Method – Practical Criteria

  • Number of records.

  • Record length (long records may favour methods that move pointers rather than data).

  • Availability of main memory (internal vs external sort).

  • Existing order: nearly-sorted arrays benefit most from insertion or bubble sort variants.

Summary Table

Algorithm

Best

Worst

Bubble Sort

O(n)O(n)

O(n2)O(n^2)

Insertion Sort

O(n)O(n)

O(n2)O(n^2)

Quick Sort

O(nlog2n)O(n\log_2 n)

O(n2)O(n^2)

Merge Sort

O(nlog2n)O(n\log_2 n)

O(nlog2n)O(n\log_2 n)

Additional qualitative analysis:

  • Bubble: best for almost sorted.

  • Insertion: generally preferable to Bubble; still special-case.

  • Quick: fastest on large random data if stability not required.

  • Merge: best theoretical guarantees but uses extra memory.

Worksheet / Exam-Style Problems

1. Recursive Fibonacci
  • Code:

IF n <= 1 RETURN n
ELSE RETURN fib(n-1)+fib(n-2)
  • (a) Time complexity: O(ϕn)O(\phi^{n}) where ϕ1.618\phi\approx1.618O(2n)O(2^{n}) (exponential).

  • (b) Explanation: each call spawns two more calls except at base cases; call tree resembles binary tree of height nn ⇒ about 2n2^n total calls.

  • (c) Optimisation: memoisation / dynamic programming stores intermediate Fibonacci numbers, reducing complexity to O(n)O(n) time and O(n)O(n) space (or O(1)O(1) space with iterative solution).

2. Searching 1,000,000 Sorted Emails
  • (i) Binary search preferred: O(log2106)20O(\log_2 10^6)≈20 comparisons vs linear search’s 10610^6.

  • (ii) If new emails are appended unsorted at the end, the list becomes partially ordered. Options:

    • Maintain full sort then use binary search (requires periodic re-sort).

    • Keep as hybrid: binary search on sorted prefix, linear search remaining tail.

    • If disorder grows, revert to full linear search or re-sort.

3. Practical Timing Task (A-Level P2)
  • Implement functions:

    • task2_2 – insertion sort.

    • task2_3 – quicksort.

  • Use Python timeit to measure on three text files.

  • Expected empirical orders of growth:

    • Insertion sort runtime ∝ N2N^2 (doubling input roughly quadruples time).

    • Quicksort runtime ∝ NlogNN\log N (doubling input < double time).

  • Discuss anomalies: quicksort slower on tiny or nearly-sorted files where insertion shines.


These bullet-point notes encapsulate all definitions, derivations, numeric examples, algorithm analyses, comparisons, and worksheet prompts required to replace the original 18-page transcript.