Design (LC "Design a X")
Scope — LC “design a X” problems — reading the required operations off the problem and picking the structure combination that makes every one of them O(1) or O(log n); the worked designs themselves live in the examples sheet. See also: design_examples.md — the twenty designs written out in full; design_patterns.md — consistent hashing, rate limiters and load balancing, asked in the same rounds but not LC problems; ood_design.md — class modelling, SOLID and design patterns for the LLD round; iterator.md — the iterator contract in isolation; hash_map.md and heap.md — the structures most designs combine.
LeetCode Problem Lists
0) Concept
0-1) Types
- Data Structure Design: Design custom data structures (Stack, Queue, HashMap, etc.)
- Cache Design: LRU, LFU, Time-based cache systems
- System Component Design: File systems, search systems, rate limiters
- Social Network Design: Twitter, Instagram feed, following/follower systems
- Scheduling/Booking Design: Calendar, meeting rooms, parking systems
- Stream/Iterator Design: Data stream processing, custom iterators
- Game Design: Tic-Tac-Toe, Snake, game boards
0-2) Pattern
Pattern 1: HashMap + LinkedList
- Use Case: Order-sensitive operations (LRU, LFU, insertion order)
- Examples: LRU Cache, LFU Cache, Insert Delete GetRandom O(1)
- Key Point: HashMap provides O(1) lookup, LinkedList provides O(1) ordering operations
Pattern 2: HashMap + Heap
- Use Case: Priority-based operations, top-k problems
- Examples: Design Twitter, Top K Frequent Elements in stream
- Key Point: HashMap tracks data, Heap maintains priority order
Pattern 3: Trie (Prefix Tree)
- Use Case: Autocomplete, prefix search, word validation
- Examples: Search Autocomplete System, Add and Search Word, Design Search System
- Key Point: Efficient prefix-based operations O(L) where L is word length
Pattern 4: OOD (Object-Oriented Design)
- Use Case: Complex system with multiple components and interactions
- Examples: Parking Lot, Elevator System, Library Management
- Key Point: Focus on classes, interfaces, relationships, and SOLID principles
Pattern 5: Stream/Queue Based
- Use Case: Real-time data processing, moving window operations
- Examples: Moving Average, Hit Counter, Rate Limiter
- Key Point: Deque or Queue for time-window based operations
1) General form
1-1) Basic OP
Step 1: Clarify Requirements
- What operations need to be supported?
- What are the time/space complexity requirements?
- What are the edge cases? (empty input, duplicates, concurrency)
- What is the expected scale? (single machine vs distributed)
Step 2: Choose Data Structures
- Map key requirements to appropriate data structures
- Consider trade-offs (time vs space, simplicity vs performance)
- Multiple data structures often needed (HashMap + List, HashMap + Heap, etc.)
Step 3: Define Class Structure
class DesignName:
def __init__(self, params):
# Initialize data structures
self.data_structure1 = {}
self.data_structure2 = []
def operation1(self, params):
# Implement operation
pass
def operation2(self, params):
# Implement operation
pass
Step 4: Implement Core Operations
- Focus on the required methods
- Maintain invariants (data consistency between structures)
- Handle edge cases
Step 5: Optimize
- Identify bottlenecks
- Use appropriate data structures for O(1) operations when needed
- Consider lazy evaluation or caching
1-2) Interview Tips
Tip 1: Ask Clarifying Questions
- “Should this support concurrent access?” (Usually no for LC problems)
- “What should happen if we try to get a non-existent key?”
- “Are there any constraints on input size or range?”
- “Do we need to support deletion/updates?”
Tip 2: Start with Simple Solution
- Start with brute force using basic data structures
- Explain time/space complexity
- Then optimize based on requirements
Tip 3: Data Structure Selection
- Need fast lookup? → HashMap/HashSet
- Need ordering? → LinkedList, TreeMap, Heap
- Need both? → Combine them (HashMap + LinkedList for LRU)
- Prefix operations? → Trie
- Range queries? → Segment Tree, Binary Indexed Tree
- Time-based operations? → Queue/Deque with timestamps
Tip 4: Common Mistakes to Avoid
- Not maintaining consistency between multiple data structures
- Forgetting to handle edge cases (empty, single element, duplicates)
- Not considering time complexity of helper operations
- Over-engineering (keep it simple if requirements allow)
Tip 5: OOD Specific Tips
- Define clear interfaces and responsibilities
- Use meaningful class and method names
- Consider SOLID principles (especially Single Responsibility)
- Think about extensibility and maintainability
1-3) Things to Notice
Notice 1: OrderedDict in Python
- Combines HashMap and LinkedList functionality
move_to_end(key): O(1) operation to reorderpopitem(last=False): Remove first item (FIFO),last=Truefor LIFO- Perfect for LRU/LFU cache implementations
Notice 2: Double Data Structure Pattern
- When using multiple data structures, ensure they stay synchronized
- Example: LRU uses
cache_dict(lookup) +cache_list(order) - Always update BOTH when adding/removing/modifying
Notice 3: Dummy Nodes for LinkedList
- Use dummy head/tail nodes to simplify edge cases
- Avoids null checks for head/tail operations
- Common in LRU Cache implementations
Notice 4: Time-based Expiration
- Use timestamp + cleanup strategy
- Lazy cleanup: Remove expired items when accessed
- Eager cleanup: Use heap/queue to track expiration times
- Trade-off: Space (keeping old data) vs Time (cleanup overhead)
Notice 5: defaultdict and Counter
from collections import defaultdict, Counter
# Avoid key existence checks
followers = defaultdict(set) # Auto-creates empty set
tweet_count = defaultdict(int) # Auto-creates 0
1-4) Classic LC Problems by Category
Category 1: Cache Design Priority 3 of 5 — Worth knowing — usually a variant of a must-know pattern
- LC 146. LRU Cache (Medium) - HashMap + DoublyLinkedList
- LC 460. LFU Cache (Hard) - HashMap + OrderedDict for frequency buckets
- LC 432. All O(1) Data Structure (Hard) - HashMap + DoublyLinkedList of buckets
- LC 1756. Design Most Recently Used Queue (Medium)
Category 2: Data Structure Design
- LC 380. Insert Delete GetRandom O(1) (Medium) - HashMap + ArrayList
- LC 381. Insert Delete GetRandom O(1) - Duplicates (Hard)
- LC 211. Design Add and Search Words Data Structure (Medium) - Trie
- LC 208. Implement Trie (Prefix Tree) (Medium)
- LC 641. Design Circular Deque (Medium)
- LC 622. Design Circular Queue (Medium)
- LC 225. Implement Stack using Queues (Easy)
- LC 232. Implement Queue using Stacks (Easy)
Category 3: Stream/Time-based Design
- LC 346. Moving Average from Data Stream (Easy) - Queue
- LC 362. Design Hit Counter (Medium) - Queue with timestamps
- LC 353. Design Snake Game (Medium) - Queue + Set
- LC 1396. Design Underground System (Medium) - HashMap
- LC 981. Time Based Key-Value Store (Medium) - HashMap + Binary Search
Category 4: File System Design
- LC 1166. Design File System (Medium) - HashMap for path storage
- LC 588. Design In-Memory File System (Hard) - Trie-like nested dict structure
- LC 1244. Design A Leaderboard (Medium) - HashMap + TreeMap
Category 5: Social Network Design
- LC 355. Design Twitter (Medium) - HashMap + Heap for feed merging
- LC 1603. Design Parking System (Easy) - Simple counter
Category 6: Search/Autocomplete Design
- LC 642. Design Search Autocomplete System (Hard) - Trie + Heap
- LC 1268. Search Suggestions System (Medium) - Trie or Sorting
- LC 1146. Snapshot Array (Medium) - HashMap for snapshots
Category 7: Iterator Design
- LC 284. Peeking Iterator (Medium) - Iterator wrapper with lookahead
- LC 251. Flatten 2D Vector (Medium) - Two pointers
- LC 341. Flatten Nested List Iterator (Medium) - Stack for DFS
- LC 281. Zigzag Iterator (Medium) - Queue of iterators
Category 8: Rate Limiter Design
- LC 362. Design Hit Counter (Medium) - Sliding window
- Design Token Bucket Rate Limiter (Common interview question)
- Design Leaky Bucket Rate Limiter (Common interview question)
Category 9: Game Design
- LC 348. Design Tic-Tac-Toe (Medium) - Row/Col/Diagonal counters
- LC 353. Design Snake Game (Medium) - Queue + Set
- LC 1286. Iterator for Combination (Medium)
2) Pattern Selection
A “design a X” question is never solved by one structure. It is solved by naming the operation that would be slow with the obvious structure, then adding a second structure whose only job is to make that one operation fast. Read the required operations off the problem statement first, then pick the pair.
| Every operation must be… | The pair | Why neither half is enough alone | Worked at |
|---|---|---|---|
| O(1) get and O(1) eviction by recency | hash map + doubly linked list | the map finds the node; only a doubly linked node can unlink itself in O(1) | 1) LRU |
| O(1) get and O(1) eviction by frequency | hash map + map of frequency → linked list | frequency turns eviction into “the head of the smallest non-empty bucket” | 2) LFU, 3) All O(1) |
| O(1) insert, delete and uniform random | hash map + array, with swap-to-end deletion | random needs contiguous indices; delete needs a lookup — the swap keeps both | 4) Insert Delete GetRandom |
| O(1) push/pop plus O(1) min, max or count | stack + a parallel stack of the aggregate | the aggregate is only valid for a prefix of the stack, so it must be pushed and popped with it | 6) Min Stack |
| ordered queries — floor, ceiling, ranges | balanced BST / TreeMap (SortedDict, TreeMap) |
a hash map has no order, so overlap and neighbour queries degrade to O(n) | 7) Ordered Map |
| running median, or “k-th largest so far” | two heaps (max-heap of the low half, min-heap of the high half) | keeping the halves balanced puts the answer at the two roots | 8) Two Heaps |
| lookup “as of time T” | hash map → sorted list + binary search | values are appended in time order, so the search is over an already-sorted list | 9) Time Based KV |
| counting over a sliding time window | deque or a circular buffer of buckets | expired entries leave from the front while new ones arrive at the back | 10) Hit Counter |
| prefix / wildcard matching on strings | trie, optionally with a heap or cached top-k per node | O(L) in the query length instead of O(n·L) over all words | 11) Autocomplete, 12) Add and Search Words |
| a feed merged from k followed sources | hash map + heap over per-source cursors | merge k sorted lists, but lazily — you only need the first n |
16) Design Twitter |
| a hierarchy addressed by path | trie of path components, or a map keyed by the full path | the choice is exactly whether ls on a prefix must be supported |
13), 14) |
The four questions to ask before writing a line
- Which operations, and what complexity does each need? Write the class skeleton with the required complexity as a comment on every method before choosing structures. Most wrong answers come from optimising an operation the problem never asked to be fast.
- What is the eviction or expiry rule? Recency, frequency, and a time window need three different second structures, and the rule is what tells you which.
- Do queries need order? If any query is “nearest”, “before”, “overlapping” or “range”, a hash map cannot be the only index.
- What is the invariant, and where is it restored? For two heaps it is the size balance; for LRU it is “head is newest”; for lazy increment it is “the pending delta applies to everything below”. Name it, then make every method end by restoring it.
3) Worked Examples
Twenty designs, grouped by the structure pair each one forces, live in design_examples.md:
| Group | Problems |
|---|---|
| Caches & eviction policies | LC 146, 460, 432, 380 |
| Stacks with auxiliary state | LC 895, 155, 1381 |
| Ordered maps, heaps & time windows | LC 715, 729, 731, 732, 2034, 295, 981, 362 |
| Tries & prefix search | LC 642, 211 |
| File systems & paths | LC 1166, 588, 635 |
| Feeds, games & simulation | LC 355, 348, 353, 1396 |
4) System Design Coding Patterns
Consistent hashing, the two rate limiters and the load-balancing algorithms moved to design_patterns.md. They are asked as coding questions in the same rounds as the problems above, but none of them is a LeetCode problem, and keeping them here made this sheet’s scope two subjects wide.