Context:FIT1008_MOC, FIT2004_MOC · the measurement foundation — input size, the RAM cost model, time complexity, per-operation cost, best/worst/average cases
FIT2004 emphasis: distinguish total from auxiliary space (extra beyond the input — in-place ≡O(1) auxiliary); quote the tightest (Θ) bound available. Input size is often bit-length — for a numberk, n=⌈log2(k+1)⌉, not k.
Quick Revision
🎯 Objective: measure the resource (time/space) as a function of input size n ➔ the question is scalability as n→∞.
📦 Core Components:input sizen (often bit-length!) | RAM unit-cost steps | time complexityT(n) | case (best/worst/avg/amortised).
⚡ Key Constraint: unqualified “complexity” = worst case; the RAM unit-cost assumption breaks for variable-size keys (×CompEq).
📝 Core
1. The Resource Question
What it measures ➔ how cost grows with input size — time (elementary ops) or space (peak memory).
Function of n ➔ stated as T(n), not a single number, because we care how it scales.
Time–space trade-off ➔ extra memory buys speed (memoisation, hash tables) and vice-versa.
Bit-length trap ➔ a numberk has size ⌈log2(k+1)⌉≈log2k, notk; halving k to 1 takes Θ(logk) steps, i.e. Θ(bits).
Pseudo-polynomial ➔ a loop running k times is O(2n) in true size (Knapsack O(nW), still NP-hard).
A BOUNDED parameter is a constant ➔ if the spec caps a parameter (n≤106, arr[i]<232), it contributes Θ(1) and vanishes from the bound — the cap makes it independent of input size, not merely small.
Which symbols are free ➔ before quoting a bound, list which parameters can grow without limit; a bound may only be expressed in those. The output size can be one ➔ Output-Sensitive Complexity.
3. Running Time & RAM Model
Abstraction ➔ Random-Access Machine — each elementary op = 1 unit, O(1) random access.
Declare the unit-cost assumption ➔ ”+ is O(1)” holds only for machine-word operands; on values whose bit-length grows with n an addition costs Θ(bits) ➔ iterative Fibonacci is Θ(n)word ops but Θ(n2)bit ops, since F(n)=Θ(φn) occupies Θ(n) bits ➔ Fibonacci Sequence. The model also breaks for external-memory effects.
4. Time Complexity T(n) & Step Cost
Counting rules ➔ statement =1 | sequence sums | if = test + branch | loop = body × iters | recursion = recurrence.
CompEq factor ➔ a step is O(1) only for fixed-size keys; length-m strings ⟹ O(nlogn)⋅CompEq.
swap is O(1) ➔ 3 copies; choose a sort minimising the expensive op.
5. Best / Worst / Average Case
Definitions ➔ at fixed n: W=max, B=min, A=ED[cost], with B≤A≤W.
Best ≠ worst only on short-circuit ➔ requires an if/break/early return, else best=worst (selection sort).
Average ≠ amortised ➔ average needs a distribution; amortised is a worst-case-sequence guarantee with no probability.
6. Space: Total vs Auxiliary (FIT2004 quoting standard)
The lecture’s split ➔ space complexity = input space + auxiliary space, reported separately because only the second is the algorithm’s choice. Total space is always Ω(n) for an n-element input, so it never discriminates.
Auxiliary space ➔ any memory beyond the input — the number quoted in a complexity table; in-place≡O(1) auxiliary.
The three iterative reference cases ➔ find_min(arr) keeps one variable ⟹ input Θ(n), auxiliary O(1), in-place · build_list(n) takes a number and allocates n slots ⟹ input O(1), auxiliary Θ(n) (no recursion, still Θ(n) — output counts) · iterative binary_search ⟹ auxiliary O(1).
Time ≥ auxiliary space — always ➔ memory must be allocated and written before it counts, and each cell costs ≥1 step ⟹ an algorithm quoting Θ(n) auxiliary cannot be o(n) in time.
Tightest bound ➔ quote Θ when best = worst; reserve O for a genuine upper-bound-only claim.
⚙️ Core Implementation
🔹 Step-counting & the input-size gotcha
counting rules + bit-length trap
# T(n) counting (RAM model):def bubble_sort(the_list): n = len(the_list) # 2 steps for _ in range(n-1): # outer: n-1 for i in range(n-1): # inner: n-1 if the_list[i] > the_list[i+1]: # 3 steps swap(the_list, i, i+1) # 7 steps# T(n) = 13n^2 - 22n + 12 -> O(n^2)size = k.bit_length() # == ceil(log2(k+1)) ~ log2 k (a NUMBER's size)len([1,2,3,5,8]) # 5 (a collection's size = element count)
💡 Common Mistake:Exact polynomial is accurate but useless ➔ report O(n2); a loop running k times on number k is O(2n) (since n≈log2k), not O(n).
🔹 Cost of a step & best/worst short-circuit
swapO(1) + insertion-sort's best/worst gap
def swap(a, i, j): tmp = a[i]; a[i] = a[j]; a[j] = tmp # always 3 copies => O(1)# Insertion sort inner loop — the source of the best/worst split:while i >= 0 and a[i] > temp: # sorted -> stops at once (BEST, O(n) total) a[i + 1] = a[i] # reverse -> runs k times (WORST, O(n^2)) i -= 1
💡 Common Mistake:Best case ≠ “small input” ➔ both fix size n and differ by arrangement; if comparison costs O(m), multiply every bound by m.
⚖️ Core Decision Matrix
(Best / Average / Worst time, with the worst-case trigger.)
When It Flips: quote worst for guarantees (real-time/adversarial), average for typical (quicksort, hashing), best rarely. Average ≠ amortised — average assumes a distribution (fails on skewed inputs); amortised is a hard worst-case-sequence guarantee.
📊 Exam Execution Trace
Applied Exercise
Problem: Show why an O(nW) Knapsack DP is pseudo-polynomial, not polynomial.
size of W=log2W bits⇒O(nW)=O(n2log2W)=exponential in the encoding length of W
Final Extracted Output: polynomial in the valueW but exponential in its bit-length ⟹ pseudo-polynomial (why Knapsack stays NP-hard).
✍️ Practice
Practice 1: CountBits(x) sets bits=1 then halves x while x>1; CountTotalBits(arr[1..n]) sums CountBits over the array. Give the Θ complexity when (a) ∣arr∣=n, 0≤arr[i]≤2m−1 · (b) same but 1≤n≤106 · (c) ∣arr∣=n, 0≤arr[i]≤232−1.
Hint: First cost one CountBits, then ask which of n and m is actually allowed to grow.
Answer
Inner cost ➔ halving x until x≤1 runs ⌊log2x⌋ times ⟹ Θ(bit-length), capped at Θ(m).
(a) Θ(nm) ➔ n calls × Θ(m); both parameters unbounded, so both appear.
(b) Θ(m) ➔ n≤106 is a constant cap ⟹ n=Θ(1) ⟹ it drops out.
(c) Θ(n) ➔ arr[i]<232 caps bit-length at 32=Θ(1) ⟹ each call Θ(1).
Why:A cap kills a parameter ➔ asymptotics describe growth, and a quantity that cannot grow contributes a constant factor — exactly what Θ discards. This is why 32/64-bit arithmetic is quoted O(1) while big-integer arithmetic is not.
🧠 Active Recall
Distinguish worst-case, average-case, and amortised complexity, stressing each assumption.
Hint: Each makes a different assumption.
Answer
Short answer:Worst = max, no assumption; average = expectation over a distribution; amortised = worst-case sequence ÷ length, no probability.
Why:Guarantee vs model ➔ amortised O(1) append is a guarantee; average O(1) hash lookup assumes good hashing.
Why is " Θ(n2)" portable but "runs in 3 ms" is not?
Hint: RAM-model machine-independence.
Answer
Short answer:Θ(n2) is a property under the RAM model ⟹ identical step count across machines/compilers.
Why:Folded constants ➔ “3 ms” folds in CPU speed, compiler, and input — none generalise.
Why do selection sort's best and worst cases coincide while quicksort's diverge?
Hint: Short-circuit vs input-dependent pivot.
Answer
Short answer: selection sort never early-terminates ⟹ cost depends only on n ⟹ best=worst=Θ(n2).
Why:Pivot quality ➔ quicksort’s cost depends on the input (median Θ(nlogn), min/max Θ(n2)), so its cases separate.
Merge Sort is called an O(n)-space sort and Quick Sort an in-place one, yet both allocate. Justify both labels precisely.
Hint: Auxiliary, and count the live stack chain.
Answer
Short answer:auxiliary space is what is quoted. Merge sort needs a Θ(n) scratch array; quicksort partitions inside the array, leaving only the recursion stack, O(logn) when the smaller side recurses first.
Why:Live chain, not total calls ➔ sibling calls execute sequentially, so only one root-to-leaf path of frames exists at once; “in-place” means O(1) auxiliary excluding that stack — which is why quicksort’s label survives its O(logn) frames but degrades to Θ(n) on a worst-case pivot.
If each comparison costs O(m) but each swap is O(1), does merge sort or selection sort scale better?
Hint: Weight ops by their true cost.
Answer
Short answer: merge sort Θ(nmlogn) vs selection sort Θ(n2m) ⟹ merge sort wins.
Why:Comparison-dominated ➔ the m-amplified comparison cost dominates, so selection sort’s Θ(n) swap-thrift doesn’t help.