Course

Data Structures & Algorithms — Complete Course

Self-paced study path. Work unit-by-unit: read the concept, watch the pattern, then solve the practice problems. Aim for depth + speed.

Software engineering · staff bar · 11 units

Unit 1: Unit 1 — Arrays, Strings & Hashing

Arrays are the foundation. Master the classic window/pointer patterns: sliding window for contiguous subarray problems, two pointers for sorted/paired problems, and prefix sums to turn range-sum queries into O(1). Hash maps give O(1) lookup — they trade space for time, so always say the space cost. Strings are arrays of characters; many string problems are really two-pointer or hashing problems in disguise.

Teach: Hash-maps turn lookups into O(1)

When a problem says find a pair, duplicate or element whose complement exists, reach for a hash map (dict) or hash set. The critical insight: you trade O(n) memory to make each lookup O(1) instead of O(n). Always state both time AND space complexity. Worked example — Two Sum: Input [2,7,11,15], target 9. Step 1: element 2. Complement 9-2=7 not seen yet. Store seen[2]=0. Step 2: element 7. Complement 9-7=2 IS in seen (index 0). Answer indices [0,1]. Complexity: O(n) time, O(n) space. Order matters: only one pass is needed.

Teach: Sliding window for contiguous subarrays

For 'maximum sum/ length of a subarray/substring that satisfies a constraint', the sliding window is usually O(n): grow the window by extending the right edge; shrink from the left while the constraint is violated; track the best answer at each valid state. Fixed window (length k): slide and recompute the sum by adding right and removing left — O(n) total, no nested loop. Variable window (any valid length): expand right until constraint breaks, then move left until valid again — each element enters and leaves once, so O(n).

Teach: Prefix sums for O(1) range queries

Build prefix[i] = sum of arr[0..i-1], then range sum(arr[l..r]) = prefix[r+1] - prefix[l] in O(1). Space cost O(n). This is a pure open-sesame trick: any problem asking many range sums is flirting with prefix sums. Same idea with prefix products for range products (watch out for zeros).

Teach: Two pointers on sorted arrays

Sorted array + pair/duplicate/container problem ⇒ two pointers. Place one at 0 and one at n-1; move the pointer that gets you closer to the target. Because the array is sorted, each move provably keeps all better candidates reachable — that is what makes the O(n) bound safe. Container With Most Water: area = distance × min height; move the shorter side.

Exercises

Practice problems

Unit 2: Unit 2 — Linked Lists

Linked lists test pointer manipulation and edge-case discipline (head/tail, empty, one node). The three must-have techniques: (1) the two-pointer runner for finding the middle or Nth-from-end, (2) reversing a list iteratively with prev/curr/next, and (3) a dummy head node to avoid special-casing head removal. Many 'merge' problems are just pointer walks.

Teach: Iterative reversal in one pass

Reversal is the #1 linked-list warm-up and shows pointer hygiene. Task: reverse 1 → 2 → 3 → None. State: prev=None, curr=head(1), next=None. Iteration 1: save next=2; curr.next=prev(None); prev=curr(1); curr=next(2). Iteration 2: next=3; curr.next=prev(1); prev=2; curr=3. Iteration 3: next=None; curr.next=prev(2); prev=3; curr=None. Done. Return prev (3) as the new head. O(n) time, O(1) space. Edge cases: empty list, single node.

Teach: Dummy head removes edge-case branching

Many 'remove/insert' problems force special handling at the head. A dummy node before head makes every node a 'middle' node with a uniform prev.next update. Remove Nth node from end: compute len, walk to node (len - n - 1) in ONE pass with a dummy; dummy.next -> dummy.next.next. O(n) time, O(1) space. Always ask: must I handle head-removal specially?

Exercises

Practice problems

Unit 3: Unit 3 — Trees & Binary Search Trees

Trees are the single most common interview topic. Master recursive traversal (pre/in/post) and level-order (BFS via queue). On a BST, in-order gives a sorted sequence, and search/insert/delete are O(h). The height of a balanced tree is ~log n — that's why BSTs are fast. 'Validate BST', 'lowest common ancestor', and 'max depth' are the warm-ups; path-sum and diameter problems test whether you can thread a global answer through recursion.

Teach: Recursive traversal is a template

Every traversal is the same skeleton with a different visit position. def walk(node): if not node: return visit(node) # pre-order walk(node.left) walk(node.right) Move visit(node) after the left call for in-order (=> sorted on a BST); after both calls for post-order. O(n) time, O(h) stack space. Level-order (BFS): queue, pop front, enqueue children.

Teach: BST in-order is sorted — use the invariant

A BST keeps left < node < right everywhere. So: - In-order DFS yields sorted output — the check for 'is this a BST' must carry a (lo, hi) bound because children constrain their ancestor direction, not just the immediate parent. - Search/insert/delete are O(h) ~ O(log n) balanced, O(n) skewed. validate(node, lo=-inf, hi=+inf): node.val must be in (lo, hi); recurse left with hi=node.val, right with lo=node.val.

Exercises

Practice problems

Unit 4: Unit 4 — Graphs: BFS, DFS, Topological Sort, Shortest Path

Graphs appear constantly at the Staff bar. BFS gives shortest path in unweighted graphs and is the natural tool for 'number of islands', word-ladder, and level-order grid problems. DFS works for connectivity and backtracking. Topological sort (Kahn's algorithm) handles dependency ordering — very relevant to build systems and task scheduling. Dijkstra (with a min-heap) is the weighted shortest path; union-find is the fast way to handle dynamic connectivity / number-of-connected-components.

Teach: BFS vs DFS — pick by what you need

BFS (queue) finds the SHORTEST path in unweighted graphs and processes level-by-level (islands, word ladder, grid distances). Mark visited when ENQUEUING to avoid duplicate work. DFS (stack/recursion) explores one branch fully — good for connectivity, backtracking, topological DFS. Mark visited when visiting. Grid problems: the 4-neighbor (or 8-neighbor) moves are just offset tuples; keep bounds checks inside the traversal.

Teach: Topological sort — Kahn's algorithm

For dependency ordering (courses, build systems): count in-degrees, seed the queue with 0-in-degree nodes, pop and decrement neighbours, append to order. If order length < node count → a cycle exists (no valid ordering). O(V+E). 'Course Schedule' asks canFinish (detect cycle); 'Course Schedule II' wants the actual order.

Exercises

Practice problems

Unit 5: Unit 5 — Heaps / Priority Queues

A heap gives you the min/max element in O(log n) — that's the tool for 'top K' and 'Kth largest' problems. The classic trick is a min-heap of size K for top-K-largest (keep the K largest, eject the smallest). Priority queues are also the engine of Dijkstra and greedy scheduling. Always state: 'I use a heap so each push/pop is O(log k), and the answer is the root, O(1) to read.'

Teach: Min-heap of size K = top-K-largest

To keep the K LARGEST values with O(log k) per push and O(1) to read the answer, maintain a MIN-heap of size K: push each value; if size > K, pop the smallest. The root is the K-th largest (your answer). Why min-heap and not max? The min-heap lets you eject the smallest, keeping the largest K. 'Top K Frequent Elements' = heap keyed by frequency.

Teach: Two heaps for streaming median

Split data into a max-heap (lower half) and a min-heap (upper half). Insert to the appropriate side, rebalance so sizes differ by <= 1. Median = lower.max if sizes differ else (lower.max + upper.min)/2. O(log n) per insertion. 'Find Median from Data Stream' is the classic.

Exercises

Practice problems

Unit 6: Unit 6 — Dynamic Programming

DP is the highest-yield hard topic. The process: (1) define the state f(i) — what you're computing; (2) write the recurrence — how f(i) depends on earlier states; (3) set the base cases; (4) fill bottom-up (or memoize top-down). The most common patterns: 1-D (climbing stairs, house robber, LIS), 2-D grid (unique paths), subsequence (LCS), and knapsack (choose/subset with capacity). Always give the time complexity as states × work-per-state.

Teach: The DP recipe (state → recurrence → base → order)

1. STATE: define f(i) precisely — what you are computing at step i. 2. RECURRENCE: how f(i) depends on earlier states (include/exclude, min over predecessors...). 3. BASE: seed the simplest cases. 4. ORDER: fill bottom-up (loop) or top-down (memoized recursion). 5. COMPLEXITY: states × work-per-state. Always state it. Climbing stairs: f(i) = f(i-1) + f(i-2), f(0)=1, f(1)=1 -> O(n) time, O(1) space rolling.

Teach: Knapsack = include-or-exclude with capacity

'Can we pick a subset summing to target?' (Partition Equal Subset Sum, Coin Change II): dp[j] = whether value j is reachable. For each item, iterate capacities DESCENDING to use items once: dp[j] |= dp[j - item]. Classic 0/1 knapsack: dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight_i] + value_i).

Exercises

Practice problems

Unit 7: Unit 7 — Binary Search & Sorting

Binary search isn't just 'find in sorted array' — it's 'search over an answer space that is monotonic'. Master the off-by-one (low/high/mid invariants) and the rotated-array variants. Quickselect (partition) finds the Kth element in O(n) average. Know merge sort and quicksort well enough to describe stability and complexity, and use counting sort when the range is small.

Teach: Binary search = search over a monotonic predicate

Beyond arrays, binary search works on any monotonic predicate P(k): if P(k) is true at some k, it stays true for all larger k. Then you can binary search the smallest k where P(k) holds — 'capacity', 'time', 'speed'. Invariant recipe: lo = first feasible index, hi = first infeasible; while lo < hi: mid = (lo+hi)//2; if P(mid): hi = mid else lo = mid+1. Watch off-by-one; always test empty / single / not-found cases.

Teach: Rotated sorted array — find the pivot implicitly

In a rotated sorted array, one half is always sorted. Compare nums[mid] to nums[lo]: if sorted on the left, check membership in that range; else search the right. This gives O(log n) 'Search in Rotated Sorted Array'.

Exercises

Practice problems

Unit 8: Unit 8 — Recursion & Backtracking

Backtracking is 'try, then undo'. The skeleton: a recursive helper that builds a candidate, explores, then removes the last choice (pruning). Combination/permutation/subset problems are the canonical set. The complexity is usually O(branching^depth), so pruning matters. State it honestly and show you can cut branches.

Teach: The backtracking skeleton (try → recurse → undo)

def bt(path, options): if goal(path): record(path); return for choice in options: path.append(choice) # try bt(path, options - {choice}) # recurse path.pop() # undo Subsets: include/exclude each element. Combinations: choose with a start index. Permutations: full option set each level, skip used. Prune when the partial candidate cannot lead to a solution (N-Queens).

Exercises

Practice problems

Unit 9: Unit 9 — Greedy Algorithms

Greedy = make the locally optimal choice at each step and prove it's globally optimal (usually via exchange argument or interval scheduling). The tell: 'sort then scan', interval scheduling, and activity selection. Greedy only works when the local choice can't hurt — if a counterexample exists, it's DP instead. Say why greedy is safe for THIS problem.

Teach: Local optimal → prove global safe

Greedy picks the locally best choice and relies on an exchange argument to prove global optimality. Classic tells: sort-then-scan, interval scheduling (sort by END time), merge intervals, jump reachability. Interval scheduling: sort by end, keep the earliest-finishing non-conflicting interval — because the earliest finisher leaves the most room. If a counterexample exists, it is DP, not greedy.

Exercises

Practice problems

Unit 10: Unit 10 — Two Pointers & Sliding Window

Two pointers turn O(n^2) scans into O(n): one pointer from each end (sorted-array pair problems) or a fast/slow pair (cycle detection, in-place partition). Sliding window is the 'contiguous subarray' tool — grow the right edge, shrink the left edge to restore the invariant, and the window is always a valid contiguous slice. The tell: 'longest/shortest subarray/substring with a condition' → sliding window; 'pair/triplet with a target' on sorted input → two pointers. Trapping Rain Water and Container With Most Water are the classic two-pointer hard problems.

Teach: Two pointers — shrink the search space from both ends

On sorted input, a pair-sum target can be found in O(n): start one pointer at each end; if the sum is too small, move the left pointer right; too large, move the right pointer left. Each step eliminates one candidate. The same idea powers Trapping Rain Water (track running left/right maxima) and Container With Most Water (shrink the shorter side). Fast/slow pointers detect cycles and find the middle of a list in one pass.

Teach: Sliding window — every contiguous subarray in O(n)

For 'longest/shortest contiguous subarray/substring satisfying a condition', grow the right edge to include a new element, then shrink the left edge until the invariant holds again. The window is always a valid slice, so the answer is the best window seen. Keep a frequency map for 'no repeats' or 'at most k distinct' conditions. Fixed-size windows are simpler: slide by one, add right, remove left.

Exercises

Practice problems

Unit 11: Unit 11 — Final Review & Mock Sprint

By now you've seen every core pattern. The final unit is about recall under pressure. Build a one-page cheat sheet of pattern → data structure → complexity. Then do timed mocks: 1 problem in 30–45 minutes, talking aloud, stating the brute force, then optimizing, then the complexity. Review your weak units and re-solve the problems you missed after 48h.

Teach: How to run the final mock sprint

1. Build a one-page cheat sheet: pattern → data structure → complexity. 2. Timeboxed talk-aloud mocks (30-45 min): state brute force, then optimize, then complexity. 3. After 48h, re-solve the problems you missed (spaced repetition). 4. Use DOJO practice with focus=coding at staff difficulty to simulate the real bar.

Exercises

Practice problems

Open this course in Dojo