23 ¡ Data Structures & Algorithms (for Frontend)
The computer-science foundation, from zero: Big-O complexity, the core data structures (arrays, linked lists, hash maps, sets, stacks, queues, trees, heaps, graphs, tries), and the algorithmic patterns that recur in interviews and in real frontend work (rendering trees, virtual DOM diffing, routing, autocomplete, scheduling). Practical, JS-flavored, not academic.
Positioning
Two reasons a senior frontend engineer needs this. First, interviews â senior roles (and the Ireland/Critical-Skills track) test DSA via LeetCode-style problems. Second, and more importantly, the work itself is full of data structures: the DOM and Reactâs Fiber tree are trees (01, 05), the virtual-DOM diff is a tree algorithm, routers use tries/trees, autocomplete uses tries + debouncing, state normalization uses hash maps, the event loop uses queues (02), and Reactâs Lanes use bitmasks. Understanding complexity is also how you avoid shipping an accidental O(n²) render. This file starts from basic programming knowledge and builds the vocabulary.
Foundations: Big-O notation (complexity)
Big-O describes how an algorithmâs time or space grows as input size n grows â the shape of the cost, ignoring constants. Itâs how you compare approaches and predict scaling.
| Notation | Name | Example |
|---|---|---|
| O(1) | constant | hash map lookup, array index access |
| O(log n) | logarithmic | binary search, balanced-tree ops |
| O(n) | linear | scanning an array, a single loop |
| O(n log n) | linearithmic | good sorts (merge/quick avg), the realistic floor for comparison sorts |
| O(n²) | quadratic | nested loops over the same data (naive dedupe) |
| O(2âż) / O(n!) | exponential/factorial | naive recursion (unmemoized Fibonacci), permutations |
Key ideas: drop constants and lower-order terms (O(2n + 5) â O(n)); analyze the worst case by default (also know average/amortized); space complexity matters too (a memo trades space for time). The practical senior instinct: a nested loop over the same array is O(n²) â can a hash map make it O(n)? That single move solves a huge fraction of problems.
Deep dive: core data structures
Each entry: what it is, the cost of common operations, and where it shows up in frontend.
Array (contiguous, indexed)
- Access by index O(1); search O(n); insert/delete at end O(1) amortized, at front/middle O(n) (shifting).
- JS arrays are dynamic + heterogeneous (and engine-optimized when âpackedâ;
01). The default sequence everywhere.
Hash Map / Hash Set (Map/Set/object)
- Insert/lookup/delete O(1) average (O(n) worst with collisions). The single most useful structure for turning O(n²) into O(n): build a map for O(1) lookups instead of re-scanning.
- Frontend uses: normalizing state by id (
06), de-duplication (Set), memo caches (05), counting/grouping, adjacency lists for graphs. PreferMapover plain objects for dynamic keyed collections (true any-key, size, iteration order, no prototype pitfalls).
Linked List
- Nodes pointing to next (singly) / prev+next (doubly). Insert/delete given a node O(1); access by position O(n).
- Rare to hand-write in frontend, but Reactâs hook state is a linked list on the fiber (
05), and the fiber tree uses child/sibling/return pointers. Knowing it explains the Rules of Hooks.
Stack (LIFO)
- Push/pop O(1). Uses: the call stack (
02), undo/redo, DFS, expression parsing, browser history (back), bracket-matching. Trivially an array withpush/pop.
Queue (FIFO) & Deque
- Enqueue/dequeue O(1) (use a deque/linked list;
array.shift()is O(n)). Uses: the task/microtask queues in the event loop (02), BFS, request/animation scheduling, rate-limit buffers, Reactâs work scheduling concepts.
Tree
- Hierarchical nodes (a root, children). The DOM, the CSSOM, Reactâs Fiber tree, JSON, file systems, comment threads, routing tables are all trees.
- Binary Search Tree (BST): ordered; search/insert O(log n) when balanced, O(n) when degenerate. Balanced trees (AA/red-black/B-trees) keep it O(log n).
- Traversals: depth-first (pre/in/post-order) and breadth-first (level-order). The virtual-DOM diff and any âwalk the component/DOM treeâ operation is a traversal.
Heap / Priority Queue
- A tree keeping the min/max at the root; insert/extract O(log n), peek O(1). Uses: scheduling by priority (conceptually like Reactâs Lanes prioritization,
05), âtop-Kâ problems, Dijkstra.
Graph
- Nodes + edges (directed/undirected, weighted/unweighted). Represented as an adjacency list (a hash map of node â neighbors) or matrix.
- Uses: dependency graphs (your module graph,
14), navigation/relationships, recommendation links, state machines (06). Algorithms: BFS (shortest path in unweighted), DFS (reachability/cycles/topological sort), Dijkstra/A* (weighted shortest path), topological sort (build ordering, task scheduling).
Trie (prefix tree)
- Tree keyed by string prefixes; lookup is O(length of key), independent of how many keys exist. The structure behind autocomplete/typeahead, search suggestions, and many routers (path-prefix matching). Pair with debouncing (
02) for a real search box.
Deep dive: algorithmic patterns (the interview toolkit)
These recurring patterns solve most LeetCode-style problems and map to real tasks:
- Two pointers â one array, two indices moving toward each other or in tandem (pair-sum in a sorted array, dedupe, palindrome). Often turns O(n²) â O(n).
- Sliding window â a moving range over a sequence (longest substring without repeats, max sum of k consecutive, rate-limiting). O(n) instead of recomputing.
- Hash map for O(1) lookup â âhave I seen this / its complement?â (two-sum, grouping anagrams, first unique). The most common single trick.
- Binary search â O(log n) on sorted data (or on a monotonic answer space â âsearch the answerâ). Know the off-by-one boundaries.
- BFS / DFS â tree/graph traversal (level-order vs deep; shortest path vs reachability; flood fill; tree problems).
- Recursion + memoization â Dynamic Programming â overlapping subproblems solved once and cached (Fibonacci, edit distance, coin change). Top-down memo or bottom-up tabulation. Reactâs reconciliation and the diffing of two trees is conceptually a constrained tree-DP (React uses heuristics + keys to keep it O(n),
05). - Greedy â locally optimal choices (interval scheduling, some shortest-path). Fast but must prove correctness.
- Sorting â know that good comparison sorts are O(n log n); JS
Array.sortis a stable sort (TimSort-class in V8). Provide a comparator for objects; beware default lexicographic sorting of numbers.
Frontend-specific algorithmic moments
- Virtual DOM diffing / reconciliation (
05) â comparing two trees; keys convert an O(nÂł) general tree-diff into an O(n) heuristic. This is the reason listkeys matter. - Debounce/throttle (
02,04) â control event frequency (search, scroll, resize); time-based algorithmic control. - Virtualization / windowing â render only visible items (binary-search the scroll offset; O(visible) not O(n)) â how you list 100k rows (
15). - Memoization â
useMemo/useCallback/React Compiler cache pure computations (05); same idea as DP caching. - Normalization â store entities in a hash map by id, reference by id elsewhere (O(1) updates, no duplication;
06).
Worked example: two-sum, the canonical O(n²)âO(n) move
// â Brute force: nested loops, O(n²)
function twoSumSlow(nums, target) {
for (let i = 0; i < nums.length; i++)
for (let j = i + 1; j < nums.length; j++)
if (nums[i] + nums[j] === target) return [i, j];
}
// â
Hash map: one pass, O(n) time / O(n) space â "have I seen the complement?"
function twoSum(nums, target) {
const seen = new Map(); // value â index
for (let i = 0; i < nums.length; i++) {
const need = target - nums[i];
if (seen.has(need)) return [seen.get(need), i];
seen.set(nums[i], i);
}
}
The pattern â trade space for time with a hash map to avoid re-scanning â recurs constantly, including in real code (e.g., joining two lists by id in one pass instead of nested finds, which is a silent O(n²) in many components).
Pitfalls & gotchas
- Accidental O(n²) in React â
array.find/includesinside amapover the same data; nested loops in a render. Build aMapfirst. array.shift()/unshift()in loops â O(n) each â O(n²); use a real queue/deque or push+reverse.- Default
.sort()on numbers â sorts lexicographically ([1,10,2]); always pass a comparator. - Deep recursion on large trees/inputs â stack overflow (no reliable TCO in JS,
22); convert to iterative BFS/DFS with an explicit stack/queue. - Mutating while iterating â index/skip bugs.
- Ignoring space complexity â a memo that caches unbounded keys is a memory leak (
02). - Premature micro-optimization â readability first; optimize the algorithm (the Big-O), not the constants, and only after measuring (
15,20). - Forgetting
keysemantics â wrong/index keys break the diff heuristic and cause subtle list bugs (05).
Interview questions
- Explain Big-O. Give an O(1), O(log n), O(n), O(n log n), O(n²) example each.
- When does a hash map turn O(n²) into O(n)? Give a real frontend case.
- Array vs linked list vs hash map â operation costs and when to use each.
- Stack vs queue â and where each appears in the browser/runtime.
- Tree traversals (DFS pre/in/post vs BFS) â and how the DOM/Fiber relate.
- How do
keys change the complexity of Reactâs diff? - Explain two-pointer and sliding-window with a problem each.
- BFS vs DFS â when to choose which?
- What is memoization, and how does it relate to dynamic programming and
useMemo? - Why is
array.shift()in a loop a performance trap?
Recommendations
- Internalize Big-O; reflexively ask âis this nested loop O(n²)? Can a
Mapfix it?â - Reach for the right structure:
Map/Setfor lookups/dedupe, arrays for ordered sequences, trees for hierarchy, adjacency lists for graphs, tries for prefix search. - Learn the patterns (two-pointer, sliding window, hash-map lookup, BFS/DFS, binary search, memoization/DP) â they cover most interview problems and many real tasks.
- Normalize entity state by id; virtualize long lists; debounce/throttle high-frequency events.
- For interviews (Ireland track), practice on LeetCode/NeetCode by pattern, not by random grinding; state complexity for every solution.
- Optimize algorithms over constants, and only after profiling (
15,20).
Books & references
- âGrokking Algorithmsâ (2nd ed) â Aditya Bhargava. Illustrated, from-zero, the friendliest intro to Big-O, search, sort, graphs, DP. Best starting point.
- NeetCode (neetcode.io) â the best curated, pattern-organized practice path for interviews; free roadmap + explanations.
- âCracking the Coding Interviewâ â Gayle Laakmann McDowell. The classic interview-prep catalog.
- âThe Algorithm Design Manualâ â Steven Skiena. The practical reference once youâre past basics.
- âIntroduction to Algorithmsâ (CLRS) â the rigorous academic reference (dip in, donât read cover-to-cover).
- visualgo.net and âJavaScript Algorithmsâ (trekhleb, GitHub) â visualizations and JS implementations of every structure here.
- LeetCode â practice; filter by pattern/tag and track complexity.
Connections
01-browser-engines-and-rendering.mdâ the DOM/render trees; packed-array engine optimizations.05-react-internals-and-patterns.mdâ Fiber tree, hook linked list, reconciliation diff, keys, Lanes bitmask, memoization.02-javascript-deep-dive.mdâ call stack, task/microtask queues, closures behind memoization, debounce.06-state-management-and-stores.mdâ normalization by hash map; selectors/derivation.15-performance-and-core-web-vitals.mdâ virtualization, avoiding O(n²) renders, algorithmic perf.14-build-tools-and-bundlers.mdâ the module dependency graph and topological build ordering.22-functional-programming.mdâ recursion,reduce, memoization as DP.