5 min read
On this page

Algorithm Basics

An algorithm is a finite sequence of well-defined instructions for solving a problem or performing a computation. This file covers the foundational concepts for reasoning about algorithms.

What is an Algorithm?

Formal Properties

  1. Input: Zero or more well-defined inputs.
  2. Output: One or more well-defined outputs.
  3. Definiteness: Each step is precisely defined (unambiguous).
  4. Finiteness: Terminates after a finite number of steps.
  5. Effectiveness: Each step is basic enough to be carried out in finite time.

Algorithm vs Program

An algorithm is an abstract concept — a method for solving a problem. A program is a concrete implementation of an algorithm in a specific programming language. The same algorithm can be implemented in many languages.

Correctness

Partial vs Total Correctness

Partial correctness: If the algorithm terminates, it produces the correct result.

Total correctness: Partial correctness + guaranteed termination.

Loop Invariants

A loop invariant is a property that holds:

  1. Initialization: True before the first iteration.
  2. Maintenance: If true before an iteration, it remains true after the iteration.
  3. Termination: When the loop terminates, the invariant (combined with the exit condition) implies correctness.

Example: Insertion sort.

Invariant: At the start of iteration i, arr[0..i-1] is sorted and
           contains the same elements as the original arr[0..i-1].

Initialization: i=1, arr[0..0] is trivially sorted. ✓
Maintenance: Iteration i inserts arr[i] into the correct position
             in arr[0..i-1], resulting in sorted arr[0..i]. ✓
Termination: i=n, so arr[0..n-1] is sorted. ✓

Proving Termination

Show that a variant function (loop variant) — a non-negative integer quantity — strictly decreases with each iteration. Since it can't decrease below 0, the loop must terminate.

Example: Binary search. Variant = high - low. Each iteration, either low increases or high decreases, so the variant strictly decreases.

Algorithm Specification

Pseudocode

A human-readable description using a mix of natural language and programming constructs. Not tied to any language.

INSERTION-SORT(A, n)
    for i = 1 to n-1
        key = A[i]
        j = i - 1
        while j >= 0 and A[j] > key
            A[j+1] = A[j]
            j = j - 1
        A[j+1] = key

Describing Algorithms

Good algorithm descriptions include:

  1. Input/output specification: What is given, what is produced.
  2. High-level idea: Intuition behind the approach.
  3. Detailed steps: Pseudocode or precise natural language.
  4. Correctness argument: Why it works (invariant, induction, or informal reasoning).
  5. Complexity analysis: Time and space bounds.

Algorithm Properties

Deterministic vs Nondeterministic

Deterministic: Same input always produces the same sequence of steps and output. Most algorithms are deterministic.

Nondeterministic: May make arbitrary choices at certain steps. Used in complexity theory (NP = nondeterministic polynomial time). Randomized algorithms use controlled randomness.

In-Place vs Out-of-Place

In-place: Uses O(1) extra memory (modifies input directly). Examples: insertion sort, quicksort (with in-place partition).

Out-of-place: Requires additional memory proportional to input size. Examples: merge sort (O(n) extra), hash table construction.

Stable vs Unstable (for Sorting)

Stable: Equal elements maintain their relative order from the input. Important when sorting by multiple keys.

Stable sorts: Insertion sort, merge sort, Timsort, counting sort, radix sort. Unstable sorts: Quicksort, heapsort, selection sort.

Online vs Offline

Online: Processes input piece by piece as it arrives. Must make decisions without seeing future input. Examples: insertion sort (can sort as elements arrive), LRU cache.

Offline: Has access to the entire input before processing. Examples: merge sort, optimal page replacement.

Adaptive

An adaptive algorithm performs better when the input is partially sorted (or has other structure).

Adaptive: Insertion sort (O(n) for nearly sorted), Timsort, natural merge sort. Non-adaptive: Heapsort (always O(n log n) regardless of input order).

Complexity Measures

Time Complexity

The number of basic operations (comparisons, assignments, arithmetic) as a function of input size n.

Best case: Minimum operations over all inputs of size n. Worst case: Maximum operations — the standard measure. Average case: Expected operations over a distribution of inputs. Amortized: Average over a worst-case sequence of operations.

Space Complexity

The amount of additional memory used (beyond the input).

In-place: O(1) extra space. Linear space: O(n) extra. Note: Recursive algorithms use O(depth) stack space.

Asymptotic Notation

  • O(f(n)): Upper bound (worst case grows no faster than f).
  • Ω(f(n)): Lower bound (best case grows no slower than f).
  • Θ(f(n)): Tight bound (grows at the same rate as f).
  • o(f(n)): Strict upper bound (grows strictly slower).
  • ω(f(n)): Strict lower bound (grows strictly faster).

Detailed treatment in topic 17 (Algorithm Analysis).

Algorithm Design Paradigms

Relationships between divide and conquer, greedy, dynamic programming, and backtracking

Paradigm Key Idea Examples
Brute force Try all possibilities Exhaustive search, naive string matching
Divide and conquer Split, solve recursively, combine Merge sort, quicksort, FFT
Greedy Make locally optimal choices Dijkstra, Huffman, Kruskal
Dynamic programming Store and reuse subproblem solutions Knapsack, LCS, shortest paths
Backtracking Explore, prune infeasible branches N-queens, Sudoku, constraint satisfaction
Randomized Use randomness for efficiency/simplicity Quicksort (random pivot), Miller-Rabin
Approximation Near-optimal solution in polynomial time Vertex cover 2-approx, TSP 1.5-approx

Each paradigm is covered in detail in subsequent files.

Problem-Solving Strategy

  1. Understand the problem: Read carefully. Identify inputs, outputs, constraints.
  2. Work through examples: Small cases by hand. Edge cases.
  3. Identify the paradigm: Does it have optimal substructure (DP/greedy)? Can it be divided (D&C)? Is it a search problem (backtracking)?
  4. Design the algorithm: Start with brute force, then optimize.
  5. Prove correctness: Loop invariant, induction, or informal argument.
  6. Analyze complexity: Time and space.
  7. Implement: Translate to code. Handle edge cases.
  8. Test: Verify with examples, edge cases, stress tests.

Applications in CS

  • Software engineering: Algorithm choice directly impacts system performance, scalability, and resource usage.
  • Interviews: Algorithm design and analysis is the core of technical interviews.
  • Competitive programming: Fast problem-solving requires fluency in algorithm paradigms.
  • Systems design: Understanding algorithmic tradeoffs (time vs space, exact vs approximate) informs architecture decisions.