Real World DSA
[!NOTE] This module explores the core principles of Real World DSA, deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.
1. The Hook: From LeetCode to Production
In an interview, O(N) is enough. In production, O(N) might crash the server. Real-world engineering is about tradeoffs between Latency, Throughput, and Consistency.
2. The 4 Pillars of System Design Mastery
| Pillar | Focus | Why it matters |
|---|---|---|
| 1. Latency | Sequential vs Random I/O | Why reading from RAM is 100,000x faster than Disk. |
| 2. Throughput | Batching & buffering | How implementations like LSM Trees absorb massive write spikes. |
| 3. Availability | Replication & Sharding | Keeping the system up when nodes fail (Consistent Hashing). |
| 4. Consistency | CAP Theorem | Deciding between “Latest Data” and “System Uptime”. |
3. The Real-World Toolkit
Data Structure: HashMap + Doubly Linked List.
- HashMap: Key → Node Pointer (For O(1) lookup).
- Doubly Linked List: Maintains order.
- Head: Most Recently Used.
- Tail: Least Recently Used.
When we access an item, we move it to the Head. When we add an item, we add to Head. If full, remove Tail.
4. Rate Limiter (Token Bucket)
Requirement: Allow max K requests per second. Data Structure: Queue or Counter.
- Token Bucket: A bucket holds tokens. Refills at rate R. Request consumes a token. If empty, drop request.
5. Consistent Hashing (Load Balancing)
Requirement: Distribute keys across N servers. minimize movement when a server is added/removed. Data Structure: Sorted Map (TreeMap) / Ring.
- Hash servers and keys onto a circle (0 to 232).
- Key maps to the first server clockwise.
6. Code Example: LRU Cache
Java
import java.util.*;
class Node {
int key, val;
Node prev, next;
Node(int k, int v) { key = k; val = v; }
}
class LRUCache {
Node head = new Node(0, 0), tail = new Node(0, 0);
Map<Integer, Node> map = new HashMap<>();
int capacity;
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
private void remove(Node n) {
n.prev.next = n.next;
n.next.prev = n.prev;
}
private void add(Node n) {
Node headNext = head.next;
head.next = n;
n.prev = head;
n.next = headNext;
headNext.prev = n;
}
public int get(int key) {
if (!map.containsKey(key)) return -1;
Node n = map.get(key);
remove(n);
add(n);
return n.val;
}
public void put(int key, int value) {
if (map.containsKey(key)) remove(map.get(key));
if (map.size() == capacity) {
remove(map.remove(tail.prev.key));
}
Node n = new Node(key, value);
add(n);
map.put(key, n);
}
}
Go
// Go's standard library "container/list" implements Doubly Linked List
import "container/list"
type LRUCache struct {
Cap int
Keys map[int]*list.Element
List *list.List
}
type Pair struct { K, V int }
func Constructor(capacity int) LRUCache {
return LRUCache{
Cap: capacity,
Keys: make(map[int]*list.Element),
List: list.New(),
}
}
func (this *LRUCache) Get(key int) int {
if el, ok := this.Keys[key]; ok {
this.List.MoveToFront(el)
return el.Value.(Pair).V
}
return -1
}
func (this *LRUCache) Put(key int, value int) {
if el, ok := this.Keys[key]; ok {
el.Value = Pair{key, value}
this.List.MoveToFront(el)
} else {
el := this.List.PushFront(Pair{key, value})
this.Keys[key] = el
if this.List.Len() > this.Cap {
// Remove LRU (Back)
back := this.List.Back()
this.List.Remove(back)
delete(this.Keys, back.Value.(Pair).K)
}
}
}
7. Deep Dive Strategy Lab: Applications
Intuition Through Analogy
Think of this chapter like running a high-traffic consumer app. The goal is not to memorize a fixed trick, but to repeatedly answer:
- What is the bottleneck?
- Which constraint dominates (time, memory, latency, correctness)?
- Which representation makes the bottleneck easier to eliminate?