Computer Science › Algorithms & Software

Algorithms

0 of 32
  1. Memoization: Top-Down Dynamic Programming

    Take the exact naive recursive solution already familiar, cache each subproblem's answer the first time it's computed, and reuse it instead of recomputing — turning exponential time into polynomial with one small change.

    Read →6 sections11 min
  2. Tabulation: Bottom-Up Dynamic Programming

    Instead of recursing downward and caching along the way, fill a table of subproblem answers starting from the smallest and building up — the same subproblems, computed in the opposite direction, with no recursion at all.

    Read →6 sections10 min
  3. DP in Practice: Longest Common Subsequence

    Finding the longest sequence appearing (in order, not necessarily contiguously) in both of two strings — a two-dimensional table filled from a two-way recurrence, the canonical worked example of DP with more than one shrinking dimension.

    Read →6 sections9 min
  4. DP in Practice: 0/1 Knapsack

    Choosing which items to pack, each either fully taken or fully left behind, to maximize value under a weight limit — the canonical worked example of DP where greedy provably fails and a full table is required.

    Read →6 sections10 min
  5. DP in Practice: Longest Increasing Subsequence

    Finding the longest strictly increasing subsequence of a list — a one-dimensional DP whose naive O(n²) solution hides an O(n log n) speed-up built on the searching techniques already covered.

    Read →6 sections11 min
  6. Network Flow and the Max-Flow Problem

    A directed graph with a capacity on every edge, a source, and a sink; a valid flow must respect capacity and conserve flow at every other vertex, and max-flow asks for the assignment of largest possible value.

    Read →6 sections10 min
  7. The Ford-Fulkerson Method and Augmenting Paths

    The residual graph tracks unused forward capacity plus a backward "undo" edge for flow already sent; the method repeatedly finds an augmenting path through it and pushes flow equal to the path's bottleneck.

    Read →6 sections11 min
  8. The Max-Flow Min-Cut Theorem

    The value of any flow never exceeds the capacity of any s-t cut, and the maximum flow value always equals the minimum cut's capacity exactly, proven via Ford-Fulkerson's own termination point.

    Read →6 sections10 min
  9. Edmonds-Karp and Complexity Analysis

    Always choosing the shortest augmenting path, found by BFS on the residual graph, bounds the number of iterations to O(V·E) regardless of edge capacities, giving an overall O(V·E²) running time.

    Read →6 sections10 min
  10. Breadth-First Search

    Exploring a graph level by level with a queue, from a chosen source — the algorithm that finds shortest paths, measured in number of edges, in any unweighted graph.

    Read →6 sections13 min
  11. Depth-First Search

    Exploring a graph by plunging as deep as possible before backtracking — a different traversal order than BFS, naturally recursive, and the foundation for cycle detection and topological ordering.

    Read →6 sections12 min
  12. Cycle Detection and Topological Sort

    DFS timestamps reveal a cycle the instant one exists, and — when there isn't one — the same traversal produces a valid dependency order for every vertex in a directed acyclic graph.

    Read →6 sections11 min
  13. Weighted Shortest Paths: Problem Setup

    Once edges carry weights, BFS's "fewest edges" answer stops being the same as "cheapest path" — the setup that motivates every shortest-path algorithm that follows.

    Read →6 sections11 min
  14. Dijkstra's Algorithm

    Always expanding the closest not-yet-finalized vertex next, using the binary heap already covered as its priority queue — correct whenever every edge weight is non-negative.

    Read →6 sections13 min
  15. The Bellman-Ford Algorithm

    Dijkstra's algorithm breaks the instant an edge weight goes negative — Bellman-Ford trades speed for correctness in that case, and can even detect a negative-weight cycle that makes "shortest path" meaningless.

    Read →6 sections11 min
  16. Shortest Paths in a DAG

    When the graph is guaranteed acyclic, the topological order already covered lets shortest paths be computed in a single linear pass — no priority queue, no repeated relaxation, faster than either general algorithm.

    Read →6 sections11 min
  17. Connected Components via BFS

    Running BFS from every not-yet-visited vertex partitions an undirected graph into its connected components — a direct, practical payoff of the traversal just covered.

    Read →6 sections11 min
  18. The Greedy Paradigm

    Building a solution by always taking the locally best-looking choice and never reconsidering it — a strategy that is sometimes provably optimal and sometimes simply wrong, with no way to tell which without a proof.

    Read →6 sections12 min
  19. Minimum Spanning Trees: Problem Framing

    Connecting every vertex of a weighted graph as cheaply as possible, using exactly the spanning-tree structure already covered in discrete math, now with a cost to minimize.

    Read →6 sections16 min
  20. Kruskal's Algorithm

    Sort every edge by weight and add each one greedily, skipping any that would close a cycle — a connectivity check answered instantly by the union-find structure already covered.

    Read →6 sections12 min
  21. Prim's Algorithm

    A second, equally correct way to grow a minimum spanning tree — one vertex at a time from a single starting point, always adding the cheapest edge leaving the tree so far, using the same heap Dijkstra's algorithm relies on.

    Read →6 sections15 min
  22. Proving Greedy Optimality: Activity Selection

    Scheduling the maximum number of non-overlapping activities by always picking the one that finishes earliest — and a full exchange-argument proof that this greedy choice never costs you the optimal solution.

    Read →6 sections14 min
  23. The Divide-and-Conquer Paradigm

    Solving a problem by breaking it into smaller instances of itself, solving each independently, and combining their results — the general pattern recursion already introduced, now studied as a design strategy in its own right.

    Read →6 sections13 min
  24. Searching Beyond Binary Search

    Binary search's divide-and-conquer idea generalizes past a plain sorted array — searching a rotated sorted array in O(log n) is a genuinely different, worth-solving puzzle built on the exact same pattern.

    Read →6 sections11 min
  25. Solving Recurrences

    Turning a divide-and-conquer algorithm's self-referential running time — T(n) = 2T(n/2) + O(n), for example — into a closed-form bound, by recursion trees and pattern-matching against a few canonical shapes.

    Read →6 sections14 min
  26. Merge Sort Revisited

    The same merge sort already met as one option among several — now studied rigorously as the canonical divide-and-conquer algorithm, with its T(n) = 2T(n/2) + O(n) recurrence solved in full.

    Read →6 sections13 min
  27. Quicksort: Partitioning

    A different divide-and-conquer sort — pick a pivot, partition everything smaller to its left and everything larger to its right, then recurse on each side — all done in place, unlike merge sort.

    Read →6 sections12 min
  28. Quicksort: Average-Case Analysis and Randomization

    Quicksort's worst case is O(n²) on an already-sorted or adversarial input, but choosing the pivot at random makes that worst case astronomically unlikely, giving expected O(n log n) on any input.

    Read →6 sections13 min
  29. The Comparison-Sort Lower Bound

    Why no algorithm that sorts purely by comparing elements can beat O(n log n) in the worst case — a genuine impossibility proof, via a decision-tree argument, not just an unmet goal.

    Read →6 sections14 min
  30. Quicksort vs. Merge Sort: Choosing a Sort

    Both hit the O(n log n) lower bound on average, but for genuinely different reasons and with genuinely different trade-offs — in-place vs. extra memory, stable vs. not, worst-case guarantee vs. expected performance.

    Read →6 sections11 min
  31. Choosing a Paradigm: A Decision Guide

    A closing, side-by-side guide across every paradigm covered — divide-and-conquer, greedy, and dynamic programming — organized by the tell-tale signs that point to each one, and worked through on problems that could plausibly go more than one way.

    Read →6 sections11 min

Algorithms

Part of the Computer Science curriculum.

Sort
1

The Dynamic Programming Paradigm

A problem qualifies for dynamic programming when it has overlapping subproblems (naive recursion redoes the same work over and over) and optimal substructure (an optimal solution is built from optimal solutions to its subproblems).

2

Memoization: Top-Down Dynamic Programming

Take the exact naive recursive solution already familiar, cache each subproblem's answer the first time it's computed, and reuse it instead of recomputing — turning exponential time into polynomial with one small change.

3

Tabulation: Bottom-Up Dynamic Programming

Instead of recursing downward and caching along the way, fill a table of subproblem answers starting from the smallest and building up — the same subproblems, computed in the opposite direction, with no recursion at all.

4

DP in Practice: Longest Common Subsequence

Finding the longest sequence appearing (in order, not necessarily contiguously) in both of two strings — a two-dimensional table filled from a two-way recurrence, the canonical worked example of DP with more than one shrinking dimension.

5

DP in Practice: 0/1 Knapsack

Choosing which items to pack, each either fully taken or fully left behind, to maximize value under a weight limit — the canonical worked example of DP where greedy provably fails and a full table is required.

6

DP in Practice: Longest Increasing Subsequence

Finding the longest strictly increasing subsequence of a list — a one-dimensional DP whose naive O(n²) solution hides an O(n log n) speed-up built on the searching techniques already covered.

7

Network Flow and the Max-Flow Problem

A directed graph with a capacity on every edge, a source, and a sink; a valid flow must respect capacity and conserve flow at every other vertex, and max-flow asks for the assignment of largest possible value.

8

The Ford-Fulkerson Method and Augmenting Paths

The residual graph tracks unused forward capacity plus a backward "undo" edge for flow already sent; the method repeatedly finds an augmenting path through it and pushes flow equal to the path's bottleneck.

9

The Max-Flow Min-Cut Theorem

The value of any flow never exceeds the capacity of any s-t cut, and the maximum flow value always equals the minimum cut's capacity exactly, proven via Ford-Fulkerson's own termination point.

10

Edmonds-Karp and Complexity Analysis

Always choosing the shortest augmenting path, found by BFS on the residual graph, bounds the number of iterations to O(V·E) regardless of edge capacities, giving an overall O(V·E²) running time.

11

Breadth-First Search

Exploring a graph level by level with a queue, from a chosen source — the algorithm that finds shortest paths, measured in number of edges, in any unweighted graph.

12

Depth-First Search

Exploring a graph by plunging as deep as possible before backtracking — a different traversal order than BFS, naturally recursive, and the foundation for cycle detection and topological ordering.

13

Cycle Detection and Topological Sort

DFS timestamps reveal a cycle the instant one exists, and — when there isn't one — the same traversal produces a valid dependency order for every vertex in a directed acyclic graph.

14

Weighted Shortest Paths: Problem Setup

Once edges carry weights, BFS's "fewest edges" answer stops being the same as "cheapest path" — the setup that motivates every shortest-path algorithm that follows.

15

Dijkstra's Algorithm

Always expanding the closest not-yet-finalized vertex next, using the binary heap already covered as its priority queue — correct whenever every edge weight is non-negative.

16

The Bellman-Ford Algorithm

Dijkstra's algorithm breaks the instant an edge weight goes negative — Bellman-Ford trades speed for correctness in that case, and can even detect a negative-weight cycle that makes "shortest path" meaningless.

17

Shortest Paths in a DAG

When the graph is guaranteed acyclic, the topological order already covered lets shortest paths be computed in a single linear pass — no priority queue, no repeated relaxation, faster than either general algorithm.

18

Connected Components via BFS

Running BFS from every not-yet-visited vertex partitions an undirected graph into its connected components — a direct, practical payoff of the traversal just covered.

19

The Greedy Paradigm

Building a solution by always taking the locally best-looking choice and never reconsidering it — a strategy that is sometimes provably optimal and sometimes simply wrong, with no way to tell which without a proof.

20

Minimum Spanning Trees: Problem Framing

Connecting every vertex of a weighted graph as cheaply as possible, using exactly the spanning-tree structure already covered in discrete math, now with a cost to minimize.

21

Kruskal's Algorithm

Sort every edge by weight and add each one greedily, skipping any that would close a cycle — a connectivity check answered instantly by the union-find structure already covered.

22

Prim's Algorithm

A second, equally correct way to grow a minimum spanning tree — one vertex at a time from a single starting point, always adding the cheapest edge leaving the tree so far, using the same heap Dijkstra's algorithm relies on.

23

Proving Greedy Optimality: Activity Selection

Scheduling the maximum number of non-overlapping activities by always picking the one that finishes earliest — and a full exchange-argument proof that this greedy choice never costs you the optimal solution.

24

The Divide-and-Conquer Paradigm

Solving a problem by breaking it into smaller instances of itself, solving each independently, and combining their results — the general pattern recursion already introduced, now studied as a design strategy in its own right.

25

Searching Beyond Binary Search

Binary search's divide-and-conquer idea generalizes past a plain sorted array — searching a rotated sorted array in O(log n) is a genuinely different, worth-solving puzzle built on the exact same pattern.

26

Solving Recurrences

Turning a divide-and-conquer algorithm's self-referential running time — T(n) = 2T(n/2) + O(n), for example — into a closed-form bound, by recursion trees and pattern-matching against a few canonical shapes.

27

Merge Sort Revisited

The same merge sort already met as one option among several — now studied rigorously as the canonical divide-and-conquer algorithm, with its T(n) = 2T(n/2) + O(n) recurrence solved in full.

28

Quicksort: Partitioning

A different divide-and-conquer sort — pick a pivot, partition everything smaller to its left and everything larger to its right, then recurse on each side — all done in place, unlike merge sort.

29

Quicksort: Average-Case Analysis and Randomization

Quicksort's worst case is O(n²) on an already-sorted or adversarial input, but choosing the pivot at random makes that worst case astronomically unlikely, giving expected O(n log n) on any input.

30

The Comparison-Sort Lower Bound

Why no algorithm that sorts purely by comparing elements can beat O(n log n) in the worst case — a genuine impossibility proof, via a decision-tree argument, not just an unmet goal.

31

Quicksort vs. Merge Sort: Choosing a Sort

Both hit the O(n log n) lower bound on average, but for genuinely different reasons and with genuinely different trade-offs — in-place vs. extra memory, stable vs. not, worst-case guarantee vs. expected performance.

32

Choosing a Paradigm: A Decision Guide

A closing, side-by-side guide across every paradigm covered — divide-and-conquer, greedy, and dynamic programming — organized by the tell-tale signs that point to each one, and worked through on problems that could plausibly go more than one way.