Interleaved — Phase 3

Coding interleaved practice

printable — key stays hidden on paper

Instructions: Solve each problem showing your work. These problems mix complexity analysis, recurrences, and data-structure mechanics deliberately — identify the right tool before you start. Use OO, Θ\Theta, Ω\Omega notation precisely. Total: 50 marks.


1. (5 marks) Using the formal definition of Big-O, prove or disprove that 3n2+100n+7=O(n2)3n^2 + 100n + 7 = O(n^2). Give explicit constants cc and n0n_0.

2. (6 marks) Solve the recurrence T(n)=2T(n/2)+nT(n) = 2T(n/2) + n using the Master theorem. State which case applies and give the tight Θ\Theta bound.

3. (5 marks) A dynamic array doubles its capacity when full. Starting empty, you perform nn append operations. Using aggregate analysis, show the total cost is O(n)O(n) and state the amortized cost per append.

4. (4 marks) You must insert a node in the middle of a singly linked list of length nn, given only a pointer to the head. State the worst-case time and explain why it differs from a doubly linked list when you already hold the node.

5. (6 marks) Solve T(n)=3T(n/4)+n2T(n) = 3T(n/4) + n^2 using the recursion tree method. Give the cost per level and the total.

6. (5 marks) A hash table uses chaining with mm buckets holding nn keys. State the expected time for a successful search under simple uniform hashing, and explain what happens to it when the load factor α=n/m\alpha = n/m grows without resizing.

7. (5 marks) Give the best, worst, and average case time complexities of inserting into a hash table using open addressing with linear probing, and name the phenomenon that degrades the average case.

8. (5 marks) Use the substitution method to prove T(n)=2T(n/2)+nT(n) = 2T(n/2) + n is O(nlogn)O(n \log n). Guess the form and verify by induction.

9. (4 marks) Explain why deletion in an open-addressing hash table cannot simply blank the slot, and describe the tombstone fix. What is one downside of tombstones over time?

10. (5 marks) Distinguish auxiliary space from total space complexity for merge sort (top-down, recursive on arrays). Give both values.


Answer keyMark scheme & solutions

1. (Subtopic 3.1.1 — Big-O formal definition) We need c,n0>0c, n_0 > 0 such that 3n2+100n+7cn23n^2 + 100n + 7 \le c\,n^2 for all nn0n \ge n_0. For n1n \ge 1: 100n100n2100n \le 100n^2 and 77n27 \le 7n^2, so 3n2+100n+7(3+100+7)n2=110n23n^2 + 100n + 7 \le (3+100+7)n^2 = 110 n^2. Thus c=110, n0=1c = 110,\ n_0 = 1 works. Statement true: 3n2+100n+7=O(n2)3n^2+100n+7 = O(n^2). (Tighter: choose c=4c=4, then 100n+7n2100n+7 \le n^2 needs n101n \ge 101, so n0=101n_0=101, c=4c=4.) Why this method: "Prove using formal definition" ⇒ must exhibit witnesses c,n0c, n_0, not just assert dominant term.


2. (Subtopic 3.1.7 — Master theorem) a=2, b=2, f(n)=na=2,\ b=2,\ f(n)=n. Compute nlogba=nlog22=n1=nn^{\log_b a} = n^{\log_2 2} = n^1 = n. Since f(n)=Θ(nlogba)=Θ(n)f(n) = \Theta(n^{\log_b a}) = \Theta(n), Case 2 applies. T(n)=Θ(nlogbalogn)=Θ(nlogn)T(n) = \Theta(n^{\log_b a}\log n) = \boxed{\Theta(n\log n)}. Why: Recurrence is divide-by-bb form aT(n/b)+f(n)aT(n/b)+f(n) ⇒ Master theorem is the direct tool; comparing ff to nlogban^{\log_b a} selects the case.


3. (Subtopic 3.2.1 / 3.1.5 — amortized append, aggregate method) Cheap appends cost 1 each. Resizes happen at sizes 1,2,4,,2kn1,2,4,\dots,2^k \le n, copying 1+2+4++2k<2n1+2+4+\cdots+2^k < 2n elements total. Total work =n= n (appends) +<2n+ <2n (copies) <3n=O(n)< 3n = O(n). Amortized cost per append =O(n)/n=O(1)= O(n)/n = \boxed{O(1)}. Why: "Total cost of nn operations" ⇒ aggregate method: sum everything, divide by nn.


4. (Subtopic 3.2.2 / 3.2.3 — linked list insert) With only the head, reaching the middle requires traversing n/2\sim n/2 nodes: worst case Θ(n)\Theta(n). Once at the target node, the pointer splice is O(1)O(1). In a doubly linked list, if you already hold the node, you have both prev and next pointers, so insertion/deletion is O(1)O(1) with no traversal — the back-pointer removes the need to search for the predecessor. Why: Tests distinguishing access cost from splice cost; the DLL back-pointer is the discriminating feature.


5. (Subtopic 3.1.9 — recursion tree) Root cost n2n^2. At depth ii: 3i3^i subproblems each of size n/4in/4^i, cost per node (n/4i)2=n2/16i(n/4^i)^2 = n^2/16^i. Level cost =3in2/16i=(3/16)in2= 3^i \cdot n^2/16^i = (3/16)^i n^2. Sum over levels: n2i0(3/16)i=n2113/16=1613n2n^2\sum_{i\ge0}(3/16)^i = n^2 \cdot \frac{1}{1-3/16} = \frac{16}{13}n^2. Total =Θ(n2)= \boxed{\Theta(n^2)} (root-dominated geometric series). Matches Master Case 3. Why: Recursion tree explicitly shows the geometric decay per level — chosen when you want to see which level dominates.


6. (Subtopic 3.3.3 — chaining, load factor) Under simple uniform hashing, expected chain length =α=n/m= \alpha = n/m. Expected successful-search time =Θ(1+α)= \Theta(1 + \alpha). If α\alpha stays O(1)O(1) (via resizing), search is Θ(1)\Theta(1). Without resizing, α=n/m\alpha = n/m grows linearly, degrading search to Θ(n)\Theta(n). Why: Chaining performance is governed by load factor α\alpha; distinguishes it from open addressing where α<1\alpha < 1 strictly.


7. (Subtopic 3.3.4 — open addressing / linear probing)

  • Best case: O(1)O(1) — target slot empty on first probe.
  • Worst case: O(n)O(n) — long probe run wraps most of the table.
  • Average case: 12(1+11α)\approx \tfrac{1}{2}\left(1+\frac{1}{1-\alpha}\right) probes for insertion, i.e. O ⁣(11α)O\!\left(\frac{1}{1-\alpha}\right) — blows up as α1\alpha \to 1. Degrading phenomenon: primary clustering — occupied runs grow and merge. Why: Requires the three-case framing plus naming linear probing's specific failure mode (contrast with double hashing which avoids primary clustering).

8. (Subtopic 3.1.8 — substitution method) Guess T(n)cnlognT(n) \le c\,n\log n. Inductive step (assume for n/2n/2): T(n)=2T(n/2)+n2(cn2logn2)+n=cn(logn1)+n=cnlogncn+nT(n) = 2T(n/2) + n \le 2\left(c\frac{n}{2}\log\frac{n}{2}\right) + n = c n(\log n - 1) + n = c n\log n - cn + n. This is cnlogn\le c n\log n provided cn+n0-cn + n \le 0, i.e. c1c \ge 1. Base case n=2n=2: choose cc large enough to cover T(2)T(2). Hence T(n)=O(nlogn)T(n)=O(n\log n). ∎ Why: "Prove is O()O(\cdot)" with a known guess ⇒ substitution (guess + induction), not Master theorem which only gives the bound without the proof.


9. (Subtopic 3.3.5 — tombstones) Blanking a slot breaks probe sequences: a later lookup for a key whose probe path passed through the blanked slot would stop early and wrongly report "not found." Fix: mark the slot with a tombstone (DELETED sentinel) — probing continues past it during search, but insertion may reuse it. Downside: tombstones accumulate, keeping effective load factor high and lengthening probe sequences; periodic rehashing is needed to purge them. Why: Tests understanding that open addressing correctness depends on unbroken probe chains — impossible issue in chaining.


10. (Subtopic 3.1.4 — auxiliary vs total space)

  • Auxiliary space = extra memory beyond the input. Top-down merge sort allocates temporary merge buffers totaling Θ(n)\Theta(n), plus recursion stack Θ(logn)\Theta(\log n) ⇒ auxiliary =Θ(n)= \Theta(n).
  • Total space = input + auxiliary =Θ(n)+Θ(n)=Θ(n)= \Theta(n) + \Theta(n) = \Theta(n). (For contrast, an in-place algorithm like heapsort has auxiliary Θ(1)\Theta(1) but still total Θ(n)\Theta(n).) Why: Forces separating "extra used" from "everything used" — the buffer Θ(n)\Theta(n) is auxiliary, distinct from the input nn.

[
  {
    "claim": "3n^2+100n+7 <= 110 n^2 for all n>=1 (Problem 1 witnesses)",
    "code": "import sympy as sp\nn=sp.symbols('n',positive=True)\n# check minimum of 110*n**2 - (3*n**2+100*n+7) is >=0 for n>=1\nexpr=110*n**2-(3*n**2+100*n+7)\nval_at_1=expr.subs(n,1)\nderiv_positive=sp.simplify(sp.diff(expr,n)).subs(n,1)>0\nresult = bool(val_at_1>=0 and deriv_positive)"
  },
  {
    "claim": "Master theorem: T(n)=2T(n/2)+n gives Theta(n log n) (Problem 2)",
    "code": "import sympy as sp\na,b=2,2\nlog_ba=sp.log(a,b)\n# n^log_ba == n^1, f=n, so case 2 -> n^1 * log n\ncase2 = sp.simplify(log_ba-1)==0\nresult = bool(case2)"
  },
  {
    "claim": "Recursion tree sum for T(n)=3T(n/4)+n^2 equals (16/13) n^2 (Problem 5)",
    "code": "import sympy as sp\ni=sp.symbols('i',nonnegative=True)\nn=sp.symbols('n',positive=True)\nS=sp.summation((sp.Rational(3,16))**i,(i,0,sp.oo))*n**2\nresult = bool(sp.simplify(S-sp.Rational(16,13)*n**2)==0)"
  }
]