skip to content

questions

9

How do you choose between a set, a counter map, and a key-to-index map?

level: juniorimportance: must knowfreq 75%

answer

  1. Ask what the caller reads back
  2. Membership, count, or position
  3. A set throws the number away
  4. Index maps point back into the data
  5. Store the least that answers the read

basics

~20 s

Choose by what you must read back later. A set answers only "have I seen this?". A counter map answers "how many times?". A key-to-index map answers "where did it first appear?". Store the least that answers your question.

solid answer

~50 s

All three are hash-backed lookups with the same expected O(1) cost per operation, so the choice is not about speed — it is about the read you owe the caller. If the feature is a duplicate-submission guard, the only question ever asked is membership, so a set of submission tokens is the minimal fit and nothing can drift out of sync. If the feature reports how many submissions a user made, you need a map from user to count, because a set has already thrown that number away. If the feature must point back at the first submission that used a token, you need a map from token to the index or id of that occurrence. Picking the richer structure "just in case" costs memory and adds state every write path must keep correct; picking the poorer one forces a rescan of the source data later.

go deeper

for a junior

Be ready to say, in one sentence, what each of the three lets you read back later, and to pick one for a stated requirement without hedging. Naming the read first is the habit being tested.

for a middle

Explain that all three share the same expected O(1) cost, so the choice is about retained information and about how much state every write path must keep correct.

for a senior

Show the cost of getting it wrong in production: a set chosen for a guard that later needs positions forces a rescan, and an unread counter drifts silently because no code path notices it is wrong.

for a principal

Own the rule that every stored field needs a reader. Unused state on a hot write path is a maintenance liability across a team, and the cheapest structure is the one with the fewest invariants to keep true.

## Three asks that look identical Consider a submissions service. Three requirements arrive in the same sprint: 1. Reject a submission whose idempotency token has been used before. 2. Show each user how many submissions they have made this hour. 3. When a duplicate is rejected, tell the caller which earlier submission won. All three are "look something up fast", all three are satisfied by a hash-backed container with expected O(1) operations, and a candidate who has only learned "hash lookup is fast" will reach for the same thing three times. The selection question is not *how fast* but *what do I have to be able to read back*. ## The three structures, by what they retain **A set** retains membership and nothing else. `add(token)` and `contains(token)` are the whole surface. Requirement 1 is exactly this shape: the answer the guard needs is one bit. Because there is no payload, there is nothing that can be stale — a set cannot disagree with itself. **A counter map** retains a number per key. `count[user] = count[user] + 1` on write, `count[user]` on read. Requirement 2 needs this, and a set cannot be retrofitted into it: once you have stored only membership, the second occurrence of a key is indistinguishable from the fortieth. The counter is strictly more state than the set — one machine word per key — and it must be incremented on every path that accepts a submission, including the ones added next quarter. **A key-to-value map** (here, key to the index or identifier of the first occurrence) retains a pointer back into the original data. Requirement 3 needs this. Note the direction: you are indexing *from* the value you will be asked about *to* the position you will have to produce. Building the map the other way around — position to token — is the natural way the data already sits and answers nothing, because you would have to scan it. ## The failure modes this question aims at The first is *storing too little*. A set is chosen for the guard, and two weeks later the requirement becomes "tell me which submission won". Now the only way to produce the position is to rescan the source input — an O(n) pass per rejection, or a second full build. A set does not record insertion positions; "the order I added things" is not part of what a set promises, and even a container that happens to preserve insertion order tells you the order of *first insertion*, not the index in the original data unless you put it there. The second is *storing too much*. A counter map used where a set would do costs memory per key and, worse, adds a field that every write path must maintain. Unused state rots: someone adds a code path that inserts without incrementing, and the count is quietly wrong for months because nothing reads it hard enough to notice. The third is *choosing the container before naming the read*. The disciplined move in an interview is to say the read out loud first — "the only thing I ever ask this structure is whether the token is present" — and let the structure fall out of that sentence. It also makes the answer defensible when the interviewer changes the requirement mid-question, which is usually why they asked. ## Costs, honestly stated All three give O(1) *expected* time for insert and lookup, amortized over resizes; the worst case is O(n) when many keys land in the same bucket, and an adversary who can choose your keys can force that. Space is O(n) keys for the set, plus one counter or one value per key for the maps. None of the three supports ordered iteration, range queries, or "the nearest key below this one" — if the requirement contains a comparison rather than an equality, none of these three is the answer, and that is a different selection question. ## The multiset case One more variant is worth naming: a *multiset* is a counter map wearing different clothes. When you need "how many of this key are currently present" together with removal — a running count that goes down as well as up — a counter map with an explicit rule for deleting the entry at zero is the honest implementation, and forgetting that rule is a classic slow leak: keys accumulate at count zero and the structure grows without bound.

  • You built a set of seen keys and now need the first occurrence's position. What does that cost you?
    A rescan. The set retained membership only, so the position has to be recovered from the source data — an O(n) pass, or a second build of the whole structure keyed correctly. This is the cheap-to-avoid version of the mistake: naming the read before choosing the container would have made it a key-to-index map from the start.
  • When does a counter map need an explicit rule for entries that reach zero?
    Whenever counts can go down — a multiset with removals. If you decrement without deleting the entry at zero, keys accumulate forever and the map grows even though logically nothing is in it. Either delete on reaching zero, or accept and document that the key set is the set of keys ever seen.
  • Does choosing a set over a map ever change the asymptotic cost?
    No — all three are hash-backed and give expected O(1) insert and lookup, with an O(n) worst case under heavy collisions. The difference is space per key and, more importantly, which questions the structure can still answer later. Selection here is about retained information, not about time complexity.

A guest list answers only "are you on it". A tally sheet answers "how many times did you come back". A cloakroom ticket answers "where is your coat". Same desk, three different records.

saying these in an interview costs you the question

  • Says a set and a map are interchangeable
  • Assumes a set remembers insertion positions
  • Picks the richest structure just in case
  • Chooses the container before naming the read
  • Claims a map is slower than a set at lookup

context

open as a page

What pattern does each cue suggest: sorted input, longest contiguous stretch, and next-stronger-later value?

level: juniorimportance: must knowfreq 76%

basics

~20 s

A cue narrows the pattern family: sorted input points to converging pointers or binary search, a longest contiguous stretch points to a sliding window or prefix sums, and 'next stronger later value' points to a monotonic stack.

open as a page

Why is a hash map keyed by timestamp wrong for "latest slot at or before a given time"?

level: middleimportance: must knowfreq 78%

basics

~20 s

Hashing scatters keys, so a hash map holds no ordering. It answers exact-key lookups in expected O(1) but cannot find the nearest smaller key without inspecting every entry — an O(n) scan. Predecessor and range queries need an order-preserving structure.

open as a page

Union-find or repeated graph traversal for connectivity queries as edges keep arriving?

level: middleimportance: should knowfreq 52%

basics

~20 s

The interleaving decides, not the graph. If edges arrive between queries, union-find answers each merge and check in amortized near-constant time. If the edge set is known up front, one traversal labels every component and each check is a label comparison.

open as a page

Why does the shrinking sliding window break when a contiguous-stretch problem allows negative values?

level: middleimportance: should knowfreq 58%

basics

~20 s

The shrinking window assumes the total only rises when you extend right and only falls when you advance the left edge. Negative values break that monotonicity, so a discarded left endpoint can still belong to the answer.

open as a page

Sorted input is a cue, but which patterns does it license, and what decides between them?

level: middleimportance: should knowfreq 55%

basics

~20 s

Sorted input licenses three families: converging pointers for pair or combination asks, binary search for a boundary or threshold ask, and a merge pass for combining two ordered sources. The shape of the ask, not the sortedness, picks one.

open as a page

Heap, sorted list, or balanced tree for a live auction's top bid and next-k view?

level: seniorimportance: should knowfreq 58%

basics

~20 s

Count the operations first. Bids arrive constantly, the top is read constantly, the next-k view rarely. That profile kills the sorted list's O(n) inserts; a heap fits while the top read dominates, a balanced tree once the ordered next-k view turns hot.

open as a page

A fresh interview problem shows no recognizable pattern cue — how do you proceed out loud?

level: seniorimportance: should knowfreq 48%

basics

~20 s

State a correct brute force out loud with its cost, name the single repeated operation that dominates it, then ask which structure or precomputation removes that operation. The pattern falls out of the fix rather than out of recall.

open as a page

When do you reject a specialized structure and keep the linear scan over a small collection?

level: principalimportance: should knowfreq 40%

basics

~20 s

Reject it when the collection is bounded by a rule rather than by luck, no profile implicates the scan, and the structure adds an invariant every future write path must keep true. Asymptotics describe growth, not runtime at forty elements.

open as a page