Context:FIT2004_MOC · the classification that decides which algorithms are even admissible for a problem — before complexity is discussed at all · drilled through the streaming k-smallest pattern, whose answer is a size-kHeap · contrast the offline Quickselect, which needs every item resident
Quick Revision
🎯 Objective: process input one item at a time, committing to a decision without seeing the rest ➔ maintain the answer incrementally instead of computing it once at the end.
📦 Core Components:offline ➔ all N items up front, may re-read freely | online ➔ item arrives, act, discard; N may be unknown or unbounded.
⚡ Key Constraint: for the k-smallest, the heap is a max-heap, not a min-heap ➔ you need the worst admitted item at the root, because the root is the eviction threshold every new arrival is tested against.
📝 How It Works
1. Online vs Offline
Offline ➔ the whole input is available before the algorithm starts ⟹ it may scan repeatedly, sort, index, or partition destructively — Merge Sort, Quickselect, [[Heap|bottom-up build_heap]].
Online ➔ items arrive sequentially and the algorithm must hold a valid answer after every arrival, having seen no future item — insertion sort, [[Heap|add/rise]], the size-k heap below.
Why it matters ➔ (1) the stream may exceed memory ⟹ Θ(N) space is unaffordable · (2)N may be unknown or infinite ⟹ “sort it first” is not an option · (3) an answer may be required now, at every instant, not after the last item lands.
The classification precedes the bound ➔ an offline Θ(N) algorithm is worse than useless on a stream, while an online Θ(Nlogk) one solves it — feasibility outranks asymptotics ➔ Algorithmic Complexity.
2. The Streaming k-Smallest
Structure ➔ a max-heap capped at size k, holding the k smallest items seen so far.
Invariant ➔ after every arrival, the heap contains exactly the k smallest items of the prefix seen so far, and its root is the largest of them.
Admit ➔ while the heap holds <k items, insert unconditionally in Θ(logk).
Test then act ➔ once full, compare the arrival against the root only: arrival ≥ root ⟹ reject in O(1) · arrival < root ⟹ get_max to evict the root, then add the arrival ⟹ Θ(logk).
Why the root alone decides ➔ the root is the largest of the current k smallest, i.e. the weakest member. An arrival ≥ root is ≥allk members, so it cannot displace any of them; an arrival < root displaces exactly one, and the root is unambiguously the one that must go. Comparing against the other k−1 items is redundant work.
The mirror ➔ klargest ⟹ min-heap of size k, root = smallest admitted = the threshold. Always heap on the opposite extreme to the one you are collecting.
Answering “the k-th smallest” ➔ it is the root at termination; the heap yields the whole k-set for free, which Quickselect does not.
3. Cost Profile
Time Θ(Nlogk) ➔ each of N arrivals costs O(1) to reject or Θ(logk) to admit; the reject path dominates in practice once the heap has settled on small values.
Space Θ(k), independent of N ➔ this is the property that makes the algorithm online at all; no strategy storing Θ(N) can process an unbounded stream.
Single pass ➔ each item is examined exactly once and then discarded — the formal statement of “online”.
⚙️ Core Implementation
🔹 Streaming k-smallest with a size-k max-heap
k_smallest_online — one pass, O(1) rejection, no library calls
def k_smallest_online(stream, k): heap = MaxHeap() # add -> rise, get_max -> sink for item in stream: # ONE pass; stream length may be unknown if heap.length < k: heap.add(item) # fill phase: O(log k) elif item < heap.peek_max(): # ONE comparison against the threshold heap.get_max() # evict the current worst heap.add(item) # O(log k) only when it actually wins # else: item >= root -> cannot beat any of the k -> DISCARD in O(1) return heap # the k smallest; root = the k-th smallest
💡 Common Mistake:Reaching for a min-heap because the goal says “smallest” ➔ a min-heap exposes the best item at the root, which tells you nothing about who to evict; you would have to scan all k to find the worst, making every arrival Θ(k). The heap orders on the opposite extreme to the one being collected.
When It Flips: with all N resident and one rank wanted, Quickselect's Θ(N) beats Θ(Nlogk) — take it. The heap wins the moment either premise fails: unbounded N, a memory cap, or a requirement that the answer be valid at every instant. At k=N the heap degenerates to Θ(NlogN), i.e. Heapsort — so k≪N is the operating assumption.
📊 Exam Execution Trace & Applied Exercises
Manual Execution Trace
Stream 7,2,9,1,5,8,3 with k=3. Heap contents shown as a set; only the root is ordered.
Step
Arrival
vs root
Action
Heap after
Root (threshold)
0 (Init)
—
—
—
{}
—
1
7
—
fill
{7}
7
2
2
—
fill
{7,2}
7
3
9
—
fill (now full)
{9,7,2}
9
4
1
1<9
evict 9, add 1
{7,2,1}
7
5
5
5<7
evict 7, add 5
{5,2,1}
5
6
8
8≥5
reject, O(1)
{5,2,1}
5
7
3
3<5
evict 5, add 3
{3,2,1}
3
Final:{1,2,3} — the 3 smallest, with the root 3 being the 3rd smallest. The threshold is monotonically non-increasing (7→9→7→5→3 after the fill phase), so rejections get cheaper as the stream runs.
Applied Exercise
Problem: A sensor emits N=109 readings; report the k=100 smallest under a memory cap that forbids storing the stream. Justify the algorithm and quote both bounds.
sort-then-take[[Quickselect]]size-k max-heap:Θ(NlogN),Θ(N)=Θ(109) space⟹infeasible:Θ(N) time, but requires all N resident⟹infeasible:Θ(Nlogk)=Θ(109×7),Saux=Θ(k)=Θ(100)
Final Extracted Output: the size-k max-heap — the only candidate whose space is Θ(k) rather than Θ(N). Note the winner is asymptotically slower in time than Quickselect; the constraint that selects it is space and the online requirement, not speed.
⚠️ Common Mistakes
💡 Calling Quickselect online because it is fast ➔ it partitions the entire array, so it needs every item before it can emit anything; Θ(N) time does not make an algorithm streamable.
💡 Comparing the arrival against all k heap items ➔ destroys the O(1) reject path and makes each arrival Θ(k); the heap exists precisely so that one comparison suffices.
💡 Quoting Θ(NlogN) ➔ the heap is capped at k, so operations cost Θ(logk); the bound only degenerates to Θ(NlogN) when k=Θ(N).
🧠 Active Recall
To collect the ksmallest items you use a max-heap. Justify the inversion.
Hint: Ask which item you need constant-time access to — the best or the worst.
Answer
Short answer: The operation you repeat is eviction of the worst admitted item, so the worst must be at the root.
Why:The root is the admission threshold ➔ in a max-heap of the k smallest, the root is their maximum; an arrival ≥ root is ≥ all k and is rejected in O(1), an arrival < root replaces exactly that root. A min-heap would expose the global smallest — an item you never need to touch — and force a Θ(k) scan to find the eviction victim.
State the invariant of the size- k heap and show that one comparison per arrival preserves it.
Hint: Compare the item against the boundary of the admitted set, not its interior.
Answer
Short answer:The heap holds exactly the k smallest of the prefix seen so far. The root is the boundary, so testing against it is necessary and sufficient.
Why:Maintenance in two cases ➔ if arrival ≥ root then arrival ≥ every heap member, so the k smallest of the extended prefix are unchanged ⟹ reject preserves it; if arrival < root then arrival belongs to the new k smallest and the old root does not ⟹ the swap preserves it. No third case exists, so no further comparison can add information ➔ Invariant.
Which of insertion sort, Merge Sort and [[Heap|bottom-up build_heap]] are online, and what does that predict about their use on a stream?
Hint: Ask whether each holds a valid answer after every single arrival.
Answer
Short answer:Insertion sort is online; merge sort and bottom-up heap construction are offline.
Why:Incrementality ➔ insertion sort absorbs a new last element in one pass while keeping the prefix sorted throughout, so it is always answer-valid ➔ Sorting Problem. Merge sort must split the whole array before combining anything, and Θ(n)build_heap requires all elements placed before sinking begins — on a stream it must be replaced by n online add calls at Θ(nlogn) ➔ Heap.