Algorithms — intro

Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: An algorithm is a STEP-BY-STEP recipe for solving a problem. Sorting, searching, traversing, finding the shortest path — each has well-known algorithms. You’ll rarely write them; you’ll constantly USE them.


In plain English

An algorithm is a recipe — a sequence of steps that, when followed, solves a specific problem. “Sort these numbers” is a problem; the steps to do it (compare pairs, swap, repeat) is an algorithm.

The same problem can have MANY algorithms with different trade-offs:

  • Bubble sort — simple to understand, terribly slow for large lists
  • Quick sort — fast on average, can be slow on bad input
  • Merge sort — predictable speed, uses more memory
  • Tim sort — JavaScript’s Array.sort() underneath; optimized for real-world data

Each has different time + space trade-offs. Picking the right algorithm for the situation is what computer scientists do. For webapp work, you almost always use the BUILT-IN one (array.sort(), Map.get(), etc.) which is already a well-chosen implementation.

This entry covers the algorithms you’ll HEAR ABOUT and need to recognize by name — sorting, searching, traversal, graph algorithms — without implementing them. The literacy matters for:

  • Reading AI-generated code’s comments / explanations
  • Understanding tech articles
  • Spotting when an inefficient approach is being used
  • Talking with developers about performance

For the language of “how fast / how much memory,” see Time & space complexity (Big-O). For the data structures algorithms operate on, see Data structures — overview.


Why it matters

Three reasons even a non-coder benefits:

  1. Names unlock comprehension. When code or documentation mentions “binary search” or “depth-first traversal,” knowing what they ARE turns mystery into mechanics.

  2. Decisions are easier with vocabulary. “Why is this slow?” — “It’s doing a linear search where a hash lookup would be O(1).” Knowing the words = participating in the conversation.

  3. AI tools assume basic algorithmic literacy. Claude generates code; sometimes the code is a known algorithm. Recognizing it (“oh, that’s a sliding window”) tells you what to expect.

The trade-off: full algorithm study is a college course. This entry is the 90/10 — the algorithms that appear most often in casual technical conversation.


The big categories

1. Sorting

Putting items in order. Famous algorithms by name:

  • Bubble sort — compare each pair, swap if wrong order, repeat. Simple; very slow. O(n²).
  • Insertion sort — walk through, insert each item into the right spot of a sorted list. Fast for small data; slow for large. O(n²).
  • Quick sort — pick a “pivot,” partition, recurse. Fast average case. O(n log n) typical, O(n²) worst.
  • Merge sort — split in half, sort each half, merge. Reliable; uses extra memory. O(n log n).
  • Heap sort — uses a heap data structure. O(n log n).
  • Tim sort — what JavaScript and Python use. Hybrid of merge + insertion. O(n log n) but extremely fast for partially-sorted data.

In practice: array.sort() is your sort. Never write one from scratch unless you’re studying.

2. Searching

Finding a specific item in a collection.

  • Linear search — check each item until you find it. O(n). What array.indexOf() does.
  • Binary search — only works on SORTED data. Repeatedly halve the search range. O(log n). Logarithmic — for 1 million items, only 20 checks.
Find 47 in a sorted array of 1 million numbers:
- Check middle (500,000th item). Is it 47? Larger or smaller?
- Eliminate half. Check middle of remaining 500,000.
- After 20 halvings, you're at one item.

If you can SORT once and SEARCH many times, sort first, then binary-search.

3. Hashing (lookup by key)

The fastest way to find data when you have a unique key.

  • Hash table / hash map — converts keys to integer indices via a “hash function,” stores at that position. O(1) average.

This is what JavaScript’s Map and Object do under the hood. Bedrock of fast key-based lookup.

4. Graph traversal

A graph is a set of nodes connected by edges. Friend networks, the web, dependencies in code, file systems — all graphs.

Two ways to walk through:

  • Depth-first search (DFS) — go as deep as possible, then backtrack. Like exploring a maze by always going forward until stuck. Uses a STACK (often the call stack, via recursion).
  • Breadth-first search (BFS) — explore level by level. Find all nodes at distance 1, then distance 2, etc. Uses a QUEUE.
Graph:        A
            /   \
           B     C
          /     / \
         D     E   F

DFS order:  A B D C E F
BFS order:  A B C D E F

When you see “tree traversal” — DFS or BFS variants.

Shortest path:

  • Dijkstra’s algorithm — find shortest path from one node to all others. Used in GPS, network routing.
  • A* — Dijkstra + a heuristic. Used in pathfinding for games.

5. Dynamic programming

A technique for problems with overlapping sub-problems. Solve once, cache the result, reuse.

Classic example: Fibonacci. fib(40) = fib(39) + fib(38). Without caching, you compute fib(38) over a billion times. With caching: linear time.

// Without DP — exponential time
function fib(n) {
  if (n < 2) return n;
  return fib(n - 1) + fib(n - 2);
}
 
// With DP (memoization) — linear time
const memo = new Map();
function fib(n) {
  if (n < 2) return n;
  if (memo.has(n)) return memo.get(n);
  const result = fib(n - 1) + fib(n - 2);
  memo.set(n, result);
  return result;
}

The same idea underlies database query caching, React’s useMemo, image caching, etc.

6. Divide and conquer

Solve a big problem by breaking it into smaller copies of the same problem. Merge sort, quick sort, binary search all use this.

Pseudo-code:

function solve(problem):
  if problem is small enough: solve directly
  else:
    split into smaller problems
    solve each (recursively)
    combine the answers

Tightly related to recursion. See Recursion.

7. Greedy algorithms

Make the locally-best choice at each step; hope it leads to the globally-best answer. Often works; sometimes doesn’t.

Examples:

  • Coin change (when coin sizes work nicely): always use the largest coin that fits
  • Activity selection: pick the activity that ends earliest, then next compatible, etc.

Many real-world heuristics are greedy. They’re fast but can miss optimal solutions for tricky problems.


A concrete example: solving a real problem

Task: “I have 10,000 user records. Build a UI search-as-you-type that shows matching users in real-time.”

Naive approach:

function search(query) {
  return users.filter(u => u.name.toLowerCase().includes(query.toLowerCase()));
  // For each keystroke: scan all 10,000 users
}

O(n × q) per query, where n = number of users, q = query length. For 10k users × 5-character query, ~50k operations per keystroke. Feels laggy on slow devices.

Better — build an INDEX once:

// Build an inverted index: word → users containing that word
const index = new Map();
for (const user of users) {
  for (const word of user.name.toLowerCase().split(/\s+/)) {
    if (!index.has(word)) index.set(word, []);
    index.get(word).push(user);
  }
}
 
function search(query) {
  const word = query.toLowerCase();
  return index.get(word) ?? [];
  // Lookup: O(1)
}

For prefix matching, you’d use a trie (a specialized tree). For fuzzy matching, edit distance algorithms. For full-text search, libraries (Lunr, Fuse, MeiliSearch, Postgres FTS) provide industrial-strength implementations.

Each level of sophistication uses different algorithms. Recognizing this — that the right algorithm choice unlocks dramatic speedups — is the value of algorithmic literacy.


The algorithms you SHOULD know by name

A practical list. You don’t need to implement them; just recognize them:

NameWhat it doesWhere you’d see it
Linear searchFind by scanningDefault behavior of indexOf
Binary searchFind in sorted dataLibrary lookups, database indexes
Quick sort / Merge sort / Tim sortSortArray.sort()
Hash tableKey-value lookup O(1)Map, Object
DFS (depth-first search)Tree/graph traversalDOM walks, file system traversal, recursion
BFS (breadth-first search)Tree/graph traversal, level by levelShortest path, “nearest neighbor”
DijkstraShortest path in weighted graphGPS, network routing
Topological sortOrder dependenciesnpm install resolution, build systems
Two pointers / sliding windowArray processingMany array problems
Memoization / DPCache repeated subproblemsReact.memo, useMemo, query caching
Binary search tree (BST)Sorted data with fast lookup + insertSome databases
B-treeSorted disk-based structureMost relational database indexes
Hash (SHA, MD5, etc.)Cryptographic / fingerprintSignatures, file integrity
Bloom filter”Probably in set” with tiny memorySpam filters, caching

Each is worth 30 seconds on Wikipedia when you encounter it. The investment pays back fast.


Algorithm patterns in modern code

Some algorithm patterns appear constantly in webapp work:

Two-pointer technique

For ordered arrays, advance two pointers to find pairs / windows.

// Find two numbers in sorted array that sum to target
function findPair(arr, target) {
  let i = 0, j = arr.length - 1;
  while (i < j) {
    const sum = arr[i] + arr[j];
    if (sum === target) return [arr[i], arr[j]];
    if (sum < target) i++;
    else j--;
  }
  return null;
}

O(n) instead of O(n²) for the naive approach.

Sliding window

For finding subsequences in arrays/strings.

// Longest substring with at most K distinct characters
function longestSubstring(s, k) {
  const counts = new Map();
  let max = 0, left = 0;
  for (let right = 0; right < s.length; right++) {
    counts.set(s[right], (counts.get(s[right]) ?? 0) + 1);
    while (counts.size > k) {
      counts.set(s[left], counts.get(s[left]) - 1);
      if (counts.get(s[left]) === 0) counts.delete(s[left]);
      left++;
    }
    max = Math.max(max, right - left + 1);
  }
  return max;
}

The “window” expands and contracts; O(n) total.

Memoization

Cache function results by input:

const memoize = (fn) => {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) return cache.get(key);
    const result = fn(...args);
    cache.set(key, result);
    return result;
  };
};
 
const fib = memoize((n) => n < 2 ? n : fib(n - 1) + fib(n - 2));

React’s useMemo and useCallback are domain-specific memoization. React.memo memoizes COMPONENTS.


Common gotchas

  • JavaScript’s Array.sort() does NOT sort numerically by default. [10, 2, 30].sort() gives [10, 2, 30] (strings!). For numbers: arr.sort((a, b) => a - b). This catches everyone at some point.

  • Sorting strings with localeCompare handles accented characters correctly. [].sort((a, b) => a.localeCompare(b)). Don’t try to roll your own.

  • Array.indexOf(NaN) returns -1. NaN doesn’t equal itself. Use array.findIndex(Number.isNaN).

  • Object.keys returns strings ALWAYS. Even for numeric keys.

  • Recursive algorithms can blow the stack. JavaScript has a stack limit (~10,000 frames). Deep recursion → stack overflow. Convert to iteration for large input.

  • for loops are sometimes faster than forEach/map/reduce. Modern V8 optimizes most cases, but for hot loops with simple bodies, classic for can be 2-3× faster. Profile, don’t assume.

  • Hash tables have worst-case O(n). When collisions accumulate (bad hash function, adversarial input). For typical use, you’ll never notice. For security-critical paths (rate limiting by user-controlled keys), be aware.

  • Set.has() is fast; array.includes() is slow. For repeated membership checks on large data, convert to a Set first.

  • Sort stability matters. “Stable” sort preserves relative order of equal items. JavaScript’s sort is stable (since ES2019). Useful when sorting by multiple criteria.

  • Spreading large arrays / objects in tight loops is slow. [...arr, item] creates a copy. For 10M iterations, this dominates. Use arr.push(item) for mutation, or batch.

  • Binary search requires sorted data. Sorting first is O(n log n); if you only search once, linear search (O(n)) is faster.

  • Math.random() is not cryptographically secure. For tokens, IDs, security purposes: use crypto.randomUUID() (modern) or crypto.randomBytes(...) (Node).

  • Approximation algorithms exist. For NP-hard problems (traveling salesman, scheduling), the best-known algorithms run in exponential time. Approximations get “good enough” answers in polynomial time.

  • Most “algorithm problems” you see online (LeetCode etc.) are interview prep. They’re rare in real webapp work. Real problems usually involve picking the right LIBRARY, not writing new algorithms.

  • Database queries are algorithms in disguise. Postgres’s query planner picks an algorithm (index scan vs. seq scan vs. nested loop vs. hash join). EXPLAIN ANALYZE shows what it chose. Knowing this transforms database tuning.

  • Caching IS memoization. Function-level memoization, query caching, CDN caching, HTTP caching — all the same idea at different scales.

  • AI tools write decent algorithms by default. When asking Claude to “find duplicates in a list,” you’ll get a Set-based solution. Watch for the rare case it picks O(n²) when O(n) exists.

  • Reading classic algorithms in textbook languages can be misleading. “C-style” arrays with manual indices are fine in Python pseudocode but rarely match real JS idiom. Adapt to the language.

  • Big-O is asymptotic. A “slower” O(n²) algorithm can beat a “faster” O(n log n) one for small n (constant factors). Profile before optimizing.

  • Premature optimization is real. “This might be slow at 10M items” matters; “this might be 5% slower at 100 items” rarely does. Profile real bottlenecks.

  • Most webapp slowness isn’t algorithmic. It’s network latency, database query plans, missing indexes, or rendering thrashing. Algorithm choice matters less than you’d think for “user-perceived speed.”


When algorithm choice REALLY matters

The cases where picking the right algorithm changes everything:

  • Data above ~10,000 items. Below that, even O(n²) is fast enough.
  • Hot paths. Code that runs on every keystroke, every frame, every request.
  • Database queries. The query planner makes algorithm decisions for you, but bad indexes force bad algorithms.
  • Real-time systems. Games, financial trading, anywhere milliseconds matter.

For typical Bible Quest-scale projects: algorithm choice almost never matters. The slowness is elsewhere. But knowing the vocabulary is still valuable.


See also


Sources