Coding interleaved practice
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 , , notation precisely. Total: 50 marks.
1. (5 marks) Using the formal definition of Big-O, prove or disprove that . Give explicit constants and .
2. (6 marks) Solve the recurrence using the Master theorem. State which case applies and give the tight bound.
3. (5 marks) A dynamic array doubles its capacity when full. Starting empty,
you perform append operations. Using aggregate analysis, show the total
cost is 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 , 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 using the recursion tree method. Give the cost per level and the total.
6. (5 marks) A hash table uses chaining with buckets holding keys. State the expected time for a successful search under simple uniform hashing, and explain what happens to it when the load factor 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 is . 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 such that for all . For : and , so . Thus works. Statement true: . (Tighter: choose , then needs , so , .) Why this method: "Prove using formal definition" ⇒ must exhibit witnesses , not just assert dominant term.
2. (Subtopic 3.1.7 — Master theorem) . Compute . Since , Case 2 applies. . Why: Recurrence is divide-by- form ⇒ Master theorem is the direct tool; comparing to selects the case.
3. (Subtopic 3.2.1 / 3.1.5 — amortized append, aggregate method) Cheap appends cost 1 each. Resizes happen at sizes , copying elements total. Total work (appends) (copies) . Amortized cost per append . Why: "Total cost of operations" ⇒ aggregate method: sum everything, divide by .
4. (Subtopic 3.2.2 / 3.2.3 — linked list insert)
With only the head, reaching the middle requires traversing nodes:
worst case . Once at the target node, the pointer splice is .
In a doubly linked list, if you already hold the node, you have both prev
and next pointers, so insertion/deletion is 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 . At depth : subproblems each of size , cost per node . Level cost . Sum over levels: . Total (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 . Expected successful-search time . If stays (via resizing), search is . Without resizing, grows linearly, degrading search to . Why: Chaining performance is governed by load factor ; distinguishes it from open addressing where strictly.
7. (Subtopic 3.3.4 — open addressing / linear probing)
- Best case: — target slot empty on first probe.
- Worst case: — long probe run wraps most of the table.
- Average case: probes for insertion, i.e. — blows up as . 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 . Inductive step (assume for ): . This is provided , i.e. . Base case : choose large enough to cover . Hence . ∎ Why: "Prove is " 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 , plus recursion stack ⇒ auxiliary .
- Total space = input + auxiliary . (For contrast, an in-place algorithm like heapsort has auxiliary but still total .) Why: Forces separating "extra used" from "everything used" — the buffer is auxiliary, distinct from the input .
[
{
"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)"
}
]