Bigger Tool, Bigger Hunt
Ugh. Week 5 to Week 8 now. Tool get bigger โ Graph for connected world, Dynamic Programming for smart-remember hunt, Heap/Trie/Greedy for special job. Also start touch System Design โ how big cave (system) built for many many hunter.
Graph โ BFS, DFS, Topological Sort, Union-Find
Not simple family-tree anymore. Graph โ cave with many path, some path go back, some cross each other. Node connect to any other node, not just one parent-child.
โถ Live Hunt: BFS vs DFS on Same Graph
BFS vs DFS
BFS โ use Queue. Explore level-by-level, ring outward. Best for shortest path (unweighted).
DFS โ use Stack (or recursion call-stack). Go deep one path first, backtrack when stuck. Best for path-exist, cycle-detect, topological order.
Complexity
| Algorithm | Time | Space |
|---|---|---|
| BFS / DFS | O(V+E) | O(V) |
| Topological Sort | O(V+E) | O(V) |
| Union-Find (path compress) | O(ฮฑ(n)) โ O(1) | O(V) |
// BFS โ level by level using Queue function bfs(graph, start) { const visited = new Set([start]); const queue = [start]; const order = []; while (queue.length) { const node = queue.shift(); order.push(node); for (const neighbor of graph[node]) { if (!visited.has(neighbor)) { visited.add(neighbor); queue.push(neighbor); } } } return order; } // Topological Sort โ order that respect dependency function topoSort(graph, nodes) { const visited = new Set(), stack = []; function dfs(node) { visited.add(node); for (const n of graph[node] || []) if (!visited.has(n)) dfs(n); stack.push(node); // push after all children done } for (const n of nodes) if (!visited.has(n)) dfs(n); return stack.reverse(); } // Union-Find โ track connected group, detect cycle in undirected graph class UnionFind { constructor(n) { this.parent = Array.from({length:n}, (_, i) => i); } find(x) { if (this.parent[x] !== x) this.parent[x] = this.find(this.parent[x]); // path compression return this.parent[x]; } union(a, b) { this.parent[this.find(a)] = this.find(b); } }
Dynamic Programming โ Fibonacci to Knapsack
Recursion waste time re-solve same small problem again and again. DP remember answer first time (memo), never solve same small problem twice. Big speed gain.
โถ Live Hunt: 0/1 Knapsack Table Fill
Items: (weight 1, value 6), (weight 3, value 10), (weight 4, value 12). Bag capacity = 5.
Two Style of DP
Top-Down (Memoization) โ recursion + cache. Natural to write, still use call-stack.
Bottom-Up (Tabulation) โ build table from small to big, no recursion, often less memory.
How Spot DP Problem
Ask: 1) Can big problem break to overlapping smaller problem? 2) Does optimal answer built from optimal smaller answer (optimal substructure)? If yes to both โ DP candidate.
// Fibonacci โ bottom-up tabulation (start easy) function fibDP(n) { const dp = [0, 1]; for (let i = 2; i <= n; i++) dp[i] = dp[i-1] + dp[i-2]; return dp[n]; } // 0/1 Knapsack โ grow to hard function knapsack(weights, values, capacity) { const n = weights.length; const dp = Array.from({length:n+1}, () => new Array(capacity+1).fill(0)); for (let i = 1; i <= n; i++) { for (let w = 0; w <= capacity; w++) { if (weights[i-1] <= w) { dp[i][w] = Math.max( dp[i-1][w], // skip item values[i-1] + dp[i-1][w - weights[i-1]] // take item ); } else { dp[i][w] = dp[i-1][w]; // too heavy, must skip } } } return dp[n][capacity]; }
Heap
Pile of stone, but special rule โ biggest (or smallest) stone always sit on top, easy grab. Not fully sorted, just top always correct.
Complexity
| Operation | Time |
|---|---|
| Get min/max (peek) | O(1) |
| Insert | O(log n) |
| Remove min/max | O(log n) |
| Build heap from array | O(n) |
When Use Heap
"Kth largest/smallest," "top K frequent," "merge K sorted list," Dijkstra shortest path, median-finder, task scheduler by priority.
// Min-Heap core operation (array-based) class MinHeap { constructor() { this.heap = []; } peek() { return this.heap[0]; } push(val) { this.heap.push(val); let i = this.heap.length - 1; while (i > 0) { const parent = Math.floor((i-1)/2); if (this.heap[parent] <= this.heap[i]) break; [this.heap[parent], this.heap[i]] = [this.heap[i], this.heap[parent]]; i = parent; } } // pop() would "bubble down" the last element to restore heap order โ similar idea reversed }
Trie (Prefix Tree)
Tree built from letter, not number. Each path from root spell out word. Share common prefix โ "cat" and "car" share "ca" branch.
Complexity
| Operation | Time |
|---|---|
| Insert word | O(L) |
| Search word | O(L) |
| Prefix search | O(L) |
L = length of word. No depend on how many word stored!
When Use Trie
Autocomplete, spell-checker, IP routing table, word-search game, "longest common prefix" problem.
class TrieNode { constructor() { this.children = {}; this.isEnd = false; } } class Trie { constructor() { this.root = new TrieNode(); } insert(word) { let node = this.root; for (const ch of word) { if (!node.children[ch]) node.children[ch] = new TrieNode(); node = node.children[ch]; } node.isEnd = true; } search(word) { let node = this.root; for (const ch of word) { if (!node.children[ch]) return false; node = node.children[ch]; } return node.isEnd; } }
Greedy
Hunter grab best-looking stone right now, never look back, never change mind. Fast, but only work if "best now" always lead to "best overall."
Greedy vs DP
Greedy make one choice, move on, never reconsider โ fast but only correct for special-structure problem.
DP consider all choice, keep best โ slower but always correct if overlapping subproblem exist.
Classic Greedy Problem
Activity selection (max non-overlap meeting), coin change (with canonical coin system), Huffman encoding, minimum spanning tree (Kruskal/Prim โ use Union-Find!).
// Activity Selection โ max number of non-overlapping meeting function maxActivities(activities) { // sort by end time โ greedy rule: always pick earliest-finishing next activities.sort((a, b) => a.end - b.end); let count = 1, lastEnd = activities[0].end; for (let i = 1; i < activities.length; i++) { if (activities[i].start >= lastEnd) { count++; lastEnd = activities[i].end; } } return count; }
Big-O Review
Stop. Look back all tool learned. Big-O โ how hunt-time grow when pile of stone grow bigger. This week: no new tool, just sharpen eye for speed.
Complexity Ladder (Fast โ Slow)
| Big-O | Name | Example Tool |
|---|---|---|
| O(1) | Constant | Hash lookup, array index |
| O(log n) | Logarithmic | Binary search, balanced BST |
| O(n) | Linear | Single loop, linear search |
| O(n log n) | Linearithmic | Merge sort, heap sort |
| O(nยฒ) | Quadratic | Nested loop, bubble sort |
| O(2โฟ) | Exponential | Naive recursion (no memo) |
Load Balancer
Too many hunter come to one small cave-entrance, jam happen. Load Balancer stand at front door, send each hunter to different entrance, spread the crowd even.
Round Robin
Send request to server 1, 2, 3, 1, 2, 3... in turn. Simple, fair if all server equal strength.
Least Connections
Send to server currently handle fewest active request. Better when request-time vary a lot.
Consistent Hashing
Same key (user-id) always land same server (mostly). Good for cache-affinity, minimize reshuffle when server added/removed.
Caching
Instead walk far storage-cave every time, keep copy of popular stone near front door (cache). Much faster grab, but must remember to update if real stone change.
Cache Eviction Policy
LRU (Least Recently Used) โ throw out stone not touched longest time.
LFU (Least Frequently Used) โ throw out stone touched fewest time.
TTL (Time To Live) โ stone auto-expire after set time.
Cache Pattern
Cache-aside โ app check cache first, if miss, fetch DB then fill cache.
Write-through โ write go to cache AND DB together, always consistent, bit slower write.
Write-back โ write go to cache first, DB update later (async), fast but risk data loss.
Database Index
Without index, database must walk every single row to find answer โ like search whole cave stone-by-stone. Index โ like table-of-content at cave entrance, jump straight to right shelf.
How Index Work
Most index use B-Tree structure underneath โ similar idea like balanced BST you learned Phase 1! Lookup become O(log n) instead of O(n) full table scan.
Trade-off
Index speed up READ (SELECT with WHERE/ORDER BY on indexed column), but slow down WRITE (INSERT/UPDATE/DELETE must also update index) and use extra storage.
Vertical vs Horizontal Scale
Cave getting crowded โ two choice. Make one cave bigger (vertical), or build more cave side by side (horizontal).
Vertical Scale (Scale Up)
Add more CPU/RAM to same machine. Simple โ no code change needed. But hit hard ceiling (machine can only grow so big), and single point of failure remain.
Horizontal Scale (Scale Out)
Add more machine, share load between them (need Load Balancer!). No real ceiling, better fault-tolerance. But need design app to be stateless, more complex.
| Aspect | Vertical | Horizontal |
|---|---|---|
| Complexity | Low | High |
| Cost curve | Exponential at top-end | Linear-ish |
| Fault tolerance | Single point of failure | Redundant |
| Ceiling | Hardware limit | Practically none |