Exercises — Applications — frequency counting, two-sum problem, caching (LRU)
This page is a self-test. Each problem sits above a collapsible solution — try it first, then open. Difficulty climbs in five levels:
Prerequisites live in the parent parent topic and in Hash Functions, Collision Resolution (Chaining vs Open Addressing), Doubly Linked List, Sliding Window, Time Complexity Analysis, and Caching Strategies.
L1 — Recognition
Exercise 1.1
You are asked: "How many times does each word appear in a document?" Which single data structure turns this into one linear pass, and what is stored as key and as value?
Recall Solution
Tool: a hash map used as a frequency map.
Key = the word (a string), value = the running count (an integer).
Why this and not sorting? Sorting all words to group duplicates costs ; a map
counts in because each lookup/increment is average (see Time Complexity Analysis).
One pass: for each word w, do count[w] += 1 (missing → default 0).
Exercise 1.2
In Two-Sum with target = 10, you are currently looking at the number x = 4. Name the exact value
you should look up in your map, and say what the map maps from and to.
Recall Solution
Look up the complement . The map stores value → index of every element strictly to the left of the current one. The question "is a valid partner already available?" is literally "is a key in the map?".
L2 — Application
Exercise 2.1
Build the frequency map for the string "mississippi" by hand. Then name the first non-repeating
character.
Recall Solution
Walk left to right, incrementing each character:
| char | map after |
|---|---|
| m | {m:1} |
| i | {m:1,i:1} |
| s | {m:1,i:1,s:1} |
| s | {m:1,i:1,s:2} |
| i | {m:1,i:2,s:2} |
| s | {m:1,i:2,s:3} |
| s | {m:1,i:2,s:4} |
| i | {m:1,i:3,s:4} |
| p | {m:1,i:3,s:4,p:1} |
| p | {m:1,i:3,s:4,p:2} |
| i | {m:1,i:4,s:4,p:2} |
Final counts: m:1, i:4, s:4, p:2. Check: length, so nothing was lost.
First non-repeating: re-scan the original string in order and return the first char whose
count is . That is m (position 0). Why two passes? Pass 1 builds totals; pass 2 uses the
original left-to-right order to break ties. Still total.
Exercise 2.2
Run the one-pass Two-Sum on nums = [3, 2, 4], target = 6. Show the table and give the answer.
Recall Solution
Recall the invariant: when we reach index , seen holds exactly nums[0..j-1].
| j | x | c = 6−x | c in seen? | seen after |
|---|---|---|---|---|
| 0 | 3 | 3 | no (seen empty) |
{3:0} |
| 1 | 2 | 4 | no | {3:0, 2:1} |
| 2 | 4 | 2 | yes → index 1 | — |
At the complement was stored at index , so the answer is (1, 2).
Note we did not match the first 3 with itself: at we checked before inserting, and
seen was empty.
Exercise 2.3
For a cache of capacity 2, run this sequence and give the final list order (front→back) and the
final map keys:
put(1,A), put(2,B), get(1), put(3,C), put(4,D), get(2).
Recall Solution
| op | list (front→back) | map keys | note |
|---|---|---|---|
| put(1,A) | 1 | {1} | new → front |
| put(2,B) | 2,1 | {1,2} | 2 newest |
| get(1)→A | 1,2 | {1,2} | touching 1 moves it to front |
| put(3,C) | 3,1 | {1,3} | full → evict back = 2 |
| put(4,D) | 4,3 | {3,4} | full → evict back = 1 |
| get(2)→-1 | 4,3 | {3,4} | 2 evicted long ago |
Final: list order 4,3; map keys {3,4}. get(2) returns -1 (miss).
Why 1 was the victim at put(4)? After put(3), the back was 1 (it hadn't been touched
since get(1)), so it became the least-recently-used and got evicted.

L3 — Analysis
Exercise 3.1
A student claims Two-Sum by "sort then two-pointer" is strictly better because it uses extra space. State the precise flaw and the exact cost of the correct hash approach.
Recall Solution
Flaw: the problem asks for the original indices, but sorting destroys them. To recover
indices you must sort (value, index) pairs, which reintroduces auxiliary space — so the
promised space is not real for this problem. Also the sort costs .
Correct hash cost: time , space for the map.
So hashing is asymptotically faster ( vs ) and keeps indices for free.
Exercise 3.2
Explain, with the pointer-surgery equations, why LRU needs a doubly linked list and not a singly linked one. Then give the two-line unlink operation.
Recall Solution
To make a touched node "most recent" in , we must first splice it out of its current spot. Splicing requires rewiring the node before it — its predecessor.
- A Doubly Linked List stores
prev, so the predecessor is known instantly: Two pointer writes → . - A singly linked list has no
prev; to find the predecessor you would scan from the head, which is . That would break the contract forget/put.
Exercise 3.3
For frequency counting over a list of items with distinct values, give the tight time and space in big-O, and state the exact worst case for space.
Recall Solution
Time: every element triggers one hash + one map update, each average, so . Space: the map holds one entry per distinct key: . Worst case space: when all values are distinct, , giving . (See Collision Resolution (Chaining vs Open Addressing) for why "average " can degrade to per op under adversarial collisions — the map may need resizing/good hashing to hold.)
L4 — Synthesis
Exercise 4.1
Longest substring without repeating characters. Combine a frequency/last-seen map with the
Sliding Window technique to solve "abcabcbb" in . Give the length and the substring.
Recall Solution
Idea: keep a window [L..R] with all-distinct chars. Store last[ch] = last index seen.
When the char at R was seen inside the window, jump L to last[ch] + 1 (never move L
backward). Track the max window width.
| R | ch | last[ch] before | new L | window | width |
|---|---|---|---|---|---|
| 0 | a | — | 0 | a | 1 |
| 1 | b | — | 0 | ab | 2 |
| 2 | c | — | 0 | abc | 3 |
| 3 | a | 0 (≥L) | 1 | bca | 3 |
| 4 | b | 1 (≥L) | 2 | cab | 3 |
| 5 | c | 2 (≥L) | 3 | abc | 3 |
| 6 | b | 4 (≥L) | 5 | cb | 2 |
| 7 | b | 6 (≥L) | 7 | b | 1 |
Max width = 3, achieved by substring "abc". Each of L, R moves forward at most
times → . The map gives "have I seen this char, and where?".
Exercise 4.2
Two-Sum with a twist: count all pairs. Given nums = [1, 5, 3, 3, 5], target = 6, count the
number of unordered index pairs (i, j) with nums[i] + nums[j] = target. Use a frequency map.
Recall Solution
Values summing to 6: pairs of complements are (1,5) and (3,3).
Build frequency map: {1:1, 5:2, 3:2}.
- Distinct complements
a + b = 6,a < b: only1 + 5. Count = pairs. - Equal halves
a = b = 3: choose 2 of the two 3's = pair. Total = unordered pairs. Why the for equal halves? A value can't pair with itself using the same index; we count distinct index pairs among the copies.
L5 — Mastery
Exercise 5.1
Design under constraint. You must support get/put in and also answer getMostRecent()
in (return the most-recently-used key). Which sentinel pointer answers it, and why is it ?
Recall Solution
Use the same map + Doubly Linked List design. Keep a head sentinel on the most-recent side.
Then getMostRecent() = head.next.k — the node immediately after the head is always the newest.
It is because it's a single pointer dereference; every get/put already maintains the
invariant "front = most recent" by splicing touched nodes right behind head. No scan needed.
(Symmetrically, the least recent is tail.prev.k, which is exactly the eviction victim.)
Exercise 5.2
Edge cases of LRU. For capacity 1, trace put(1,A), put(2,B), get(1), put(2,C),
get(2). Give every return value and the final state.
Recall Solution
| op | map | list (front→back) | return |
|---|---|---|---|
| put(1,A) | {1:A} | 1 | — |
| put(2,B) | {2:B} | 2 | — (evicted 1: over cap) |
| get(1) | {2:B} | 2 | -1 (1 was evicted) |
| put(2,C) | {2:C} | 2 | — (key exists → update value, move to front) |
| get(2) | {2:C} | 2 | C |
Final state: map {2:C}, list 2.
Why put(2,C) does not evict? Key 2 already exists, so we _remove its old node and re-add —
the map size never exceeds capacity, so no eviction fires. This is the degenerate capacity-1 case
the design must still handle correctly.
Exercise 5.3
Degenerate frequency input. What does the first-non-repeating-character algorithm return for the
empty string "", and for "aabb"? Justify from the algorithm's definition.
Recall Solution
"": the frequency map is empty; the second pass has nothing to scan, so there is no non-repeating character → return a sentinel such as-1/None. (Handle the empty case explicitly.)"aabb": frequency map{a:2, b:2}. Every count is , so the second pass finds no char with count 1 → return-1/None. The result "no unique char exists" is a valid, non-crashing outcome the caller must expect.
Recall Feynman one-liner
Frequency counting ::: a tally sheet: one line per distinct thing, add a stroke each time you see it. Two-Sum complement ::: "I need a friend who adds up to the target — have I already met them?" LRU pairing ::: the map is a phone book (find fast); the linked list is a queue of who you called last (evict fast).