Rapid-fire theory questions
Many technical interviews open with a rapid-fire round: 10-20 short questions in quick succession, 20-30 seconds each. The goal isn't to probe depth — it is to see whether the fundamentals are settled and whether you stay composed.
The answer formula — three parts, 2-3 sentences:
- 1. The direct answer — no preamble, no restating the question. "A hash map turns the key into an array index via a hash function."
- 2. A one-line WHY — the mechanism or the reason. "That's what makes lookup
O(1)on average." - 3. One concrete example — the part that separates understanding from recitation. "It's ideal for finding a user by ID, but it can't answer 'what's the smallest ID'."
All three take 20-25 seconds together. The difference between a candidate who adds the example and one who doesn't is visible in the interviewer's notes.
What not to do:
- Give a long answer — in this round that's a negative signal: it shows you didn't notice the question was simple. If they want depth they'll ask. "I can go into detail if useful" handles that politely
- Make something up — the worst option. Interviewers almost always sense it, and afterwards they doubt every other answer. "I don't know that precisely, but I'd guess..." or simply "I don't know" is far better
- Restate the question to buy time — "So you're asking how a hash map works?" It is noticeable
On rhythm: saying "I don't know" to 2-3 out of 15 is completely normal, and nobody expects 15 out of 15. What is expected is honesty and composure.
| Question — data structures | Model answer (20-30 seconds) |
|---|---|
| Difference between an array and a linked list? | An array keeps elements in contiguous memory — indexed access is `O(1)` but inserting/removing in the middle is `O(n)` because elements shift. In a linked list each node points to the next — removal is `O(1)` when you hold the node, but indexed access is `O(n)`. In practice arrays are often faster because they are cache-friendly. |
| How does a hash map work? | The key goes through a hash function, the result maps to a bucket index in an array, and the value is stored there. That is what makes lookup `O(1)` on average. It's ideal for finding a user by ID — but since it keeps no order, "the smallest ID" has no answer better than `O(n)`. |
| What is a hash collision and how is it resolved? | Two different keys landing in the same bucket — unavoidable, since the number of buckets is finite. Two main fixes: chaining (a list or tree inside the bucket) and open addressing (probe for the next free slot). With many collisions `O(1)` degrades toward `O(n)`, which is why modern implementations track the load factor and resize. |
| Difference between a stack and a queue? | A stack is LIFO — last in, first out; undo, bracket matching, an iterative rewrite of recursion. A queue is FIFO — first in, first out; task queues, BFS. Both are `O(1)` to add and remove; the only difference is which end you remove from. |
| What is a heap and what is it for? | A tree-shaped structure whose root is always the minimum (or maximum); insert and extract are `O(log n)`, peeking is `O(1)`. It is used as a priority queue: top-K, Dijkstra, task schedulers. An important nuance: a heap is not a sorted list — iterating it does not give sorted order. |
| When do you choose a set over an array? | When the question "does this value exist?" repeats. In an array that check is `O(n)`, in a set `O(1)`. The classic case is finding a duplicate: check each element against the set, `O(n)` overall instead of a nested-loop `O(n²)`. The trade-off: `O(n)` extra memory and the loss of ordering. |
| What is a binary search tree and why must it be balanced? | A tree where each node has smaller values on the left and larger on the right; a search discards half at each step — `O(log n)`. But inserting sorted data turns the tree into a chain and it becomes `O(n)`. That's why self-balancing variants (AVL, red-black) are used in practice — `TreeMap` and `SplayTreeMap` are exactly those. |
| When do you pick a tree map over a hash map? | When you need order. A hash map gives `O(1)` but is useless for range queries, min/max, "nearest value" and ordered iteration. A tree map is `O(log n)` but supports all of them. Example: "who is above this user" on a leaderboard demands a tree map. |
| Why is appending to a dynamic array called "amortised O(1)"? | Normally the append is `O(1)`, but when the array fills, a new array twice the size is allocated and everything is copied — that single operation is `O(n)`. Since the size doubles each time, those copies get rarer and rarer, so `n` appends cost `O(n)` in total, i.e. `O(1)` per operation on average. |
| What is a trie? | A prefix tree: each node is a character and the path from the root spells a prefix. Lookup depends on the prefix length, not on how many words are stored. Ideal for autocomplete and dictionary checks; in exchange it is memory hungry, so on a small dictionary a plain filter is more practical. |
| Difference between stack memory and heap memory? | The stack holds call frames and local variables; allocation is very fast and it is cleaned up automatically when the function returns, but its size is limited — deep recursion causes a stack overflow. The heap is for dynamic objects: allocation is more expensive, lifetimes are longer, and cleanup is either GC or manual. Don't confuse it with the "heap" data structure — same name, different thing. |
| Question — algorithms and complexity | Model answer (20-30 seconds) |
|---|---|
| What is Big O and why the worst case? | Big O describes how the work grows as the input grows — it drops constants and lower-order terms because they stop mattering at scale. The worst case is used because you need a guarantee: "fast on average" doesn't explain a service falling over on a ten-million-item input. Example: quicksort is `O(n log n)` on average but `O(n²)` at worst — which is exactly what makes it risky in a real-time system. |
| When is `O(n log n)` unavoidable? | When you sort by comparing elements only to each other. `n` elements have `n!` orderings and a binary decision tree must be at least `log₂(n!) ≈ n log n` tall. That is an information-theoretic bound. To get past it you must stop comparing: counting sort is `O(n + k)`, but it requires the values to lie in a narrow range. |
| BFS or DFS — which, and why? | They are the same algorithm; BFS uses a queue and DFS a stack. If the question is "minimum number of steps" or "nearest", BFS — it goes layer by layer, so the first arrival is the shortest. "Does it exist", "how many components", "is there a cycle", "produce an ordering" — DFS. Both are `O(V + E)`. |
| Why is quicksort usually faster than merge sort in practice? | Both are `O(n log n)`, but quicksort works in place with sequential memory access, using the CPU cache well. Merge sort writes into an auxiliary array at every level. Same Big O, different constant factor. Merge sort wins when stability is required, on linked lists, and in external sorting. |
| What is stability in sorting? | Elements with equal keys keep their original relative order. The practical consequence: sort by name then by department and names stay alphabetical within each department. Stable: merge sort, insertion sort, TimSort. Not stable: quicksort, heap sort. Dart's `List.sort` gives no stability guarantee — a real source of bugs. |
| Why does binary search require sorted input? | Because the decision to discard half rests on order: if `a[mid]` is below the target, you know everything to its left is too. Without order that inference is invalid. More generally, the real precondition is monotonicity — which is why binary search also works with no array at all, directly over the range of answers. |
| When do you choose recursion over iteration? | When the problem itself is recursive — tree traversal, backtracking, divide and conquer — recursion makes the code far more readable. I choose iteration when the depth can grow, because each recursive call consumes a stack frame and 10⁵ deep means a stack overflow. Some languages have tail-call optimisation, but in JavaScript you can't rely on it in practice. |
| How do you turn a nested loop (`O(n²)`) into `O(n)`? | I look at what the inner loop does: usually a search for "does this value exist" or "find the matching pair". Making that search `O(1)` with a hash map turns the whole thing into `O(n)`. Two sum is the classic case. On sorted data, two pointers replace the nested loop — `O(n)`, and with `O(1)` memory. |
| What is memoization in one sentence? | Caching the result of a function so the same arguments are never recomputed. The classic case: naive recursive Fibonacci is `O(2ⁿ)` because it recomputes the same subproblems repeatedly; memoization brings it to `O(n)`. It is the top-down form of dynamic programming. |
| What counts as `O(1)` memory? | Extra memory that does NOT depend on the input size — a few variables, pointers, counters. The input itself doesn't count. That's why two pointers is `O(1)` and a hash map is `O(n)`. Note that recursion consumes memory too — a recursion `O(log n)` deep is `O(log n)` memory, not `O(1)`, and mentioning that shows precision. |
| Why does Dijkstra fail with negative weights? | The moment Dijkstra pops a node it declares its distance final. That is only safe if edges can only lengthen a path. A negative edge lets a longer path found later lower the distance, but the node is already closed. The result is silently wrong — nothing throws. For that case you use Bellman-Ford at `O(V × E)`. |
Interview tip: preparing for a rapid-fire round happens by speaking out loud, not by reading. Cover the question column of the tables above and answer each against a 30-second timer — everything looks clear when you read silently, but forming the sentence aloud is unexpectedly hard. Second tip: on a question you don't know, instead of inventing, say "I don't know that precisely, but logically it should be..." — a well-directed guess is often scored positively, while a fabrication is always negative.