WHAT we want: given a list, know how often each value occurs.
WHY a hash map: the alternative (sort, then scan runs) is O(nlogn); counting in a map is O(n).
HOW: one pass, count[x] += 1 (defaulting missing keys to 0).
def two_sum(nums, target): seen = {} # value -> index, of elements to the LEFT for j, x in enumerate(nums): c = target - x # the partner we need if c in seen: # O(1): did it appear earlier? return (seen[c], j) seen[x] = j # only record AFTER checking -> avoids using x twice return None
class Node: def __init__(self, k, v): self.k, self.v = k, v self.prev = self.next = Noneclass LRUCache: def __init__(self, capacity): self.cap = capacity self.map = {} # key -> Node : O(1) lookup self.head = Node(0, 0) # sentinel: most-recent side self.tail = Node(0, 0) # sentinel: least-recent side self.head.next = self.tail self.tail.prev = self.head def _remove(self, n): # unlink node : O(1) n.prev.next = n.next n.next.prev = n.prev def _add_front(self, n): # insert right after head : O(1) n.next = self.head.next n.prev = self.head self.head.next.prev = n self.head.next = n def get(self, key): if key not in self.map: return -1 n = self.map[key] self._remove(n); self._add_front(n) # touched -> now most recent return n.v def put(self, key, value): if key in self.map: self._remove(self.map[key]) n = Node(key, value) self.map[key] = n self._add_front(n) if len(self.map) > self.cap: # over capacity -> evict lru = self.tail.prev # node just before tail = least recent self._remove(lru) del self.map[lru.k]
Recall Feynman: explain to a 12-year-old
Imagine a magic notebook where you can flip instantly to any name and see a number next to it.
Counting: every time you see a friend you add 1 next to their name — at the end you know who
showed up most. Two-Sum: you want two cards adding to 10; for each card you check "did I
already see a 10-minus-this card?" — the notebook answers instantly. LRU: your toy box only
fits 2 toys; you keep the toy you played with most recently at the top, and when a new toy comes
you throw out the one at the bottom that you ignored longest. The notebook tells you instantly
where each toy is.
Hashing ka asli superpower yeh hai ki ek hash map mein lookup, insert aur delete average mein
O(1) hote hain. Bas isi ek cheez se teen alag problems aasaan ho jaati hain. Pehla:
frequency counting — har element pe count[x] += 1 karo, ek hi pass mein pata chal jaata hai
kaunsa item kitni baar aaya. Pure sorting ki zarurat nahi, O(n) mein kaam ho gaya.
Doosra: Two-Sum. Yahan trick hai complement. Agar target T hai aur abhi number x dekh
rahe ho, to tumhe chahiye partner c=T−x. To question banta hai "kya yeh c maine pehle dekha
hai?" — yeh hash map se turant pata chalta hai. Isliye brute force O(n2) ki jagah hum O(n)
mein nikaal lete hain. Yaad rakho: pehle complement check karo, phir current number ko map mein
daalo — warna ek element apne aap se match ho jaayega (galti!).
Teesra: LRU cache. Sirf hash map se kaam nahi chalega, kyunki map ko order nahi pata — kaun sa
key sabse purana (least recently used) hai yeh batane ke liye usse O(n) lagega. Isliye hum hash
map ke saath ek doubly linked list jodte hain: front pe most-recent, back pe least-recent.
Map se node turant mil jaata hai, aur linked list se use front pe le jaana ya back wala evict karna
dono O(1). Doubly isliye chahiye kyunki node ko beech se nikaalne ke liye uska prev pointer
chahiye. Short trick yaad rakho: "Count, Complement, Cache" — teeno C, teeno answer ko cache
karte hain.