Part I · Foundations Chapter 1

The Role of Algorithms in Computing

What an algorithm actually is, why a correct one is not automatically a good one, and the argument that efficiency is a technology you can buy with thought rather than with hardware.

Chapter 1 is the shortest chapter in CLRS and the only one with no pseudocode in it. It is tempting to skip. Do not: it sets two definitions that the other thirty-four chapters lean on constantly — what counts as a problem versus an instance, and what it means for an algorithm to be correct — and it makes the case, with numbers, that the choice of algorithm can outweigh a thousandfold difference in hardware. Everything after this chapter is machinery in service of that claim.

Numbering follows the 4th edition (2022). Chapter 1 is essentially unchanged from the 3rd edition, apart from a new discussion of machine learning and data science at the end of §1.2, and updated hardware figures in the running-time comparison.

Contents

  1. What an algorithm is
  2. Problems, instances, and correctness
  3. What kinds of problems there are
  4. Data structures, and why there is no best one
  5. Hard problems
  6. Two models that change the rules
  7. Algorithms as a technology
  8. The comparison that makes the point
  9. Where machine learning fits
  10. Recap

What an algorithm is

CLRS gives a deliberately plain definition:

An algorithm is any well-defined computational procedure that takes some value, or set of values, as input and produces some value, or set of values, as output in a finite amount of time.

Three words in that sentence are load-bearing. Well-defined means every step is unambiguous: a human or a machine following the procedure has no choices left to make. Finite rules out procedures that spin forever. And the pairing of input with output is what lets us specify an algorithm without describing how it works — you state the relationship you want between the two, and any procedure that establishes it is a solution.

That input/output relationship is called the problem. Here is the one CLRS uses throughout Part II, stated in the book’s format:

The sorting problem Input: A sequence of n numbers ⟨a₁, a₂, …, aₙ⟩. Output: A permutation (reordering) ⟨a′₁, a′₂, …, a′ₙ⟩ of the input sequence such that a′₁ ≤ a′₂ ≤ … ≤ a′ₙ.

Notice what is not here: no mention of comparisons, swaps, recursion, or arrays. The problem says what must be true of the answer, not how to get it.

The word permutation is doing real work in that statement. Without it, the output ⟨1, 2, 3⟩ would be a valid answer to every input, since it is sorted. Requiring a permutation of the input forces the algorithm to keep exactly the elements it was given, with their multiplicities.

Problems, instances, and correctness

An instance of a problem is one specific input satisfying the problem’s input constraints. For the sorting problem, ⟨31, 41, 59, 26, 41, 58⟩ is an instance, and ⟨26, 31, 41, 41, 58, 59⟩ is the output any correct sorting algorithm must produce for it. The problem is the infinite family; the instance is one member of it.

THE PROBLEM A relation between inputs and outputs. Infinitely many instances. Input: n numbers ⟨a₁…aₙ⟩ → Output: a sorted permutation of them ONE INSTANCE ⟨31, 41, 59, 26, 41, 58⟩ THE ALGORITHM A finite sequence of unambiguous steps THE OUTPUT ⟨26, 31, 41, 41, 58, 59⟩ drawn from Correct means: for EVERY instance, it halts and the output is right. One bad instance and it is incorrect.
Figure 1.1 — The problem is the specification; the instance is one input; correctness is a claim about all instances at once.

That gives the definition of correctness:

An algorithm is correct if, for every input instance, it halts with the correct output. A correct algorithm solves the given computational problem.

Two consequences are worth sitting with. First, correctness is universally quantified — it is a statement about every instance, which is exactly why testing cannot establish it and why the book proves loop invariants instead. Second, an algorithm that fails to halt on some input is incorrect, even if it never produces a wrong answer. Non-termination is a correctness bug, not a performance bug.

Incorrect algorithms can still be useful. CLRS makes this point immediately, and it surprises people. If you can control the error rate, an incorrect algorithm is often the practical choice. The example the book points forward to is primality testing in Chapter 31: a randomized test can declare a composite number prime, but the probability is bounded and you can drive it as low as you like by repeating the test. RSA key generation is built on exactly this trade.

What kinds of problems there are

The chapter surveys where algorithms actually show up, and the list is worth keeping because each entry is a forward pointer into the book. The practical problems that need algorithmic solutions share two features: they have many candidate solutions, most of them wrong or bad, and they have practical applications that make finding the good one worth the effort.

DomainThe algorithmic problem underneathWhere CLRS covers it
Biology / genomicsAligning and matching DNA sequences; finding similar substrings in enormous stringsCh 14 (dynamic programming), Ch 32 (string matching)
The internetRouting data along good paths; indexing and searching vast page collectionsCh 22–23 (shortest paths), Ch 11 (hash tables)
Electronic commercePublic-key cryptography and digital signatures, so value can move privatelyCh 31 (number-theoretic algorithms)
Manufacturing and logisticsAllocating scarce resources to maximise a linear objectiveCh 29 (linear programming), Ch 24 (max flow)
Build systems, schedulersOrdering parts or tasks so every dependency comes firstCh 20 (topological sort of a DAG)
Signal and image processingConverting between time and frequency representations quicklyCh 30 (the FFT)
Everyday softwareKeeping a collection sorted, searchable, and cheap to updateCh 6–13 (sorting and data structures)

The shortest-path entry deserves a note, because it recurs in the chapter as an illustration. If you have a road map and want the shortest route from one intersection to another, the number of possible routes is astronomical, yet you can find the best one in time roughly proportional to the size of the map. That gap — astronomically many candidates, but a fast way to the best — is the shape of nearly every satisfying result in the book.

Data structures, and why there is no best one

The chapter introduces the second half of the book’s subject matter:

A data structure is a way to store and organize data in order to facilitate access and modifications.

Then it adds the sentence that Part III spends four chapters justifying:

No single data structure works well for all purposes. Every structure is a bargain. An array gives you constant-time access by index and pays for it with expensive insertion in the middle. A linked list reverses that trade exactly. A hash table gives expected constant-time lookup by key but abandons ordering, so it cannot answer “what is the next largest key?” A balanced search tree keeps order and pays a logarithmic factor for every operation. Knowing which bargain you are signing is most of what Part III teaches.

StructureWhat it makes cheapWhat it makes expensiveChapter
ArrayAccess by index, in Θ(1)Insert or delete in the middle, Θ(n)10
Linked listInsert or delete at a known position, Θ(1)Finding that position, Θ(n)10
Stack / queuePush and pop, or enqueue and dequeue, in Θ(1)Reaching anything not at the end10
Hash tableSearch, insert, delete in Θ(1) expectedAny ordered query; worst case degrades11
Binary search treeOrdered queries: min, max, successor, rangeΘ(n) per operation if it degenerates12
Red-black treeAll of the above in O(lg n) worst caseRebalancing code, and constant factors13
These four chapters are the ones you will reach for most often after college, which is why this recap gives Part III extra depth. Chapter 1 only names the structures; the trade-offs above are the map for chapters 10 to 13.

Hard problems

Most of CLRS is about problems we can solve efficiently. Chapter 1 flags the exception up front: some problems have no known efficient algorithm, and a large family of them — the NP-complete problems — are all equivalent in a precise sense.

The chapter gives three reasons NP-complete problems are worth your attention:

  1. No efficient algorithm has ever been found for any of them, although nobody has proved that none exists. This remains the P ≠ NP question, the most famous open problem in the field.
  2. They stand or fall together. If an efficient algorithm exists for any one NP-complete problem, then one exists for all of them. That is what “complete” means, and it is why a plausible-looking fast solution to one of them should make you suspect your own reasoning first.
  3. They sit disturbingly close to easy problems. Small changes to the statement flip a problem from tractable to NP-complete.

That third point is the one to remember, because it kills the intuition that “this looks similar to something easy, so it should be easy.”

Efficiently solvableNP-complete or NP-hardThe difference
Shortest simple path between two verticesLongest simple path between two verticesOne word
Euler tour: a cycle using every edge exactly onceHamiltonian cycle: a cycle visiting every vertex exactly onceEdges instead of vertices
Fractional knapsack, solved greedily0-1 knapsack, where items cannot be splitWhether you may take half an item
2-CNF satisfiability3-CNF satisfiabilityOne more literal per clause

Knowing an NP-complete problem when you see one is a practical skill, not an academic one. If a problem at work is NP-complete, you stop hunting for the exact fast algorithm and start choosing among the three honest alternatives: solve a restricted version, accept an approximation with a proven quality bound (Chapter 35), or accept exponential worst-case time with heuristics that behave well on your real inputs.

Two models that change the rules

The 4th edition uses Chapter 1 to flag two settings where the standard assumptions — one processor, all the input available up front — do not hold. Both get their own chapter in Part VII.

Parallelism

Clock speeds stopped climbing; core counts did not. A modern machine gets faster by doing several things at once, so an algorithm that cannot be decomposed into independent work leaves most of the machine idle. Chapter 26 develops task-parallel algorithms, where you express the available parallelism and a scheduler assigns it to cores. The analysis changes shape: instead of one running time you measure work (total operations) and span (the longest dependent chain), and their ratio bounds how much parallelism you can actually exploit.

Online algorithms

The usual model hands you the whole input before you must answer. An online algorithm receives its input piece by piece and must commit to a decision on each piece before seeing the rest, with no chance to revise. A cache eviction policy is the canonical example: you must choose what to evict now, without knowing what will be requested next. The quality measure changes too — you compare against the best possible offline algorithm that saw everything in advance, and the ratio is the competitive ratio. Chapter 27 covers this.

3rd edition readers: Chapter 27 in the 3rd edition was multithreaded algorithms, roughly the material now in Chapter 26. Online algorithms are new to the 4th edition.

Algorithms as a technology

Section 1.2 opens with a thought experiment. Suppose computers were infinitely fast and memory were free. Would you still have any reason to study algorithms?

CLRS answers yes, and the reason is worth stating carefully: you would still need to demonstrate that your method terminates and does so with the correct answer. Correctness is not a performance concern. An infinitely fast machine running an incorrect procedure produces wrong answers instantly, and an infinitely fast machine running a non-terminating procedure produces nothing at all, forever. So even in that fantasy, half of this book still applies.

But the fantasy is false, and that brings in the other half:

Computers may be fast, but they are not infinitely fast. Memory may be inexpensive, but it is not free. Computing time is therefore a bounded resource, and so is space in memory. You should use these resources wisely, and algorithms that are efficient in terms of time or space will help you do so.

The framing here is the point of the whole chapter. Efficiency is not a nice-to-have that you attend to after the program works. It is a resource-allocation discipline, and the algorithm is the lever with the longest arm.

The comparison that makes the point

CLRS backs the claim with a concrete head-to-head, and this worked example is the single thing most worth carrying out of Chapter 1. It pits two sorting algorithms you meet properly in Chapter 2 against each other, but you only need their running times here:

For small n, the smaller constant wins and insertion sort is genuinely faster. The interesting question is what happens as n grows, and CLRS stacks the deck as hard as it can against merge sort to answer it:

Computer AComputer B
Speed1010 instructions per second107 instructions per second — 1000× slower
AlgorithmInsertion sortMerge sort
ProgrammerWorld-class, coding in machine codeAverage, coding in a high-level language
CompilerNone neededInefficient
Resulting cost2n² instructions50 n lg n instructions

Every advantage that money and talent can buy sits with Computer A. Now sort ten million numbers.

Computer A — insertion sort, n = 10⁷ 2 · (10⁷)² instructions ———————————————————— = 2 × 10⁴ seconds ≈ 5.5 hours 10¹⁰ instructions/sec Computer B — merge sort, n = 10⁷ 50 · 10⁷ · lg(10⁷) instructions lg(10⁷) ≈ 23.25 ——————————————————————— ≈ 1163 seconds ≈ 20 minutes 10⁷ instructions/sec

The slower machine, the worse programmer, and the worse compiler beat the fast machine by a factor of about 17. The only thing Computer B had going for it was the algorithm.

Push the input size up by another factor of ten and the gap becomes absurd:

Input size nComputer A — insertion sortComputer B — merge sortB’s advantage
1052 seconds8.3 secondsA wins, 4×
106200 seconds100 secondsB wins, 2×
1075.5 hours20 minutesB wins, 17×
10823 days3.7 hoursB wins, 150×

Read that table left to right along the bottom two rows. Computer A’s thousandfold hardware advantage is a fixed multiplier; it shifts the curve down but does not change its shape. The term keeps growing faster than the n lg n term, so the crossover is inevitable and everything past it belongs to the better algorithm. Buying faster hardware buys you a constant factor. Choosing a better algorithm changes the exponent.

curves cross near n ≈ 190 below this, insertion sort wins 2n² 50 n lg n input size n → operations → 0
Figure 1.2 — Why the constant factor loses. A quadratic curve overtakes an n lg n curve no matter how favourable the constants, and the gap after the crossover keeps widening. This plots instruction counts only, where 2n² and 50 n lg n meet at n ≈ 190; feed in the 1000× machine-speed difference from the table above and the break-even in wall-clock time moves out to n ≈ 470,000.
Do not over-learn this. The top row of the table is real too: at n = 10⁵ under these constants Computer A still wins, and the two only break even around n ≈ 470,000. Asymptotics describe behaviour as n grows without bound, and real inputs are finite. This is exactly why production sort implementations are hybrids that switch to insertion sort on small subarrays. Chapter 3 makes the notion of “as n grows” precise so you can tell which regime you are in.

Algorithms compared with other technologies

The chapter closes 1.2 by asking whether algorithms are really that important, given everything else in a modern system: fast hardware, graphical interfaces, object-oriented systems, integrated web technologies, fast networking.

The answer is that algorithms sit underneath all of them. Hardware is designed with algorithms. Graphical interfaces depend on them. Routing in networks relies on them heavily, as do the compilers, interpreters, and assemblers that make high-level languages possible. Algorithms are, in the book’s phrase, at the core of most technologies used in contemporary computers.

And the trend runs the right way. As computers get faster and memory gets cheaper, we do not stop caring about efficiency — we point the extra capacity at larger problems, and larger n is precisely where algorithmic differences dominate. Faster hardware makes the choice of algorithm matter more, not less.

Having a solid base of algorithmic knowledge and technique is one characteristic that separates the truly skilled programmers from the novices. With modern computing technology you can accomplish some tasks without knowing much about algorithms, but with a good background in algorithms you can do much, much more.

Where machine learning fits

New in the 4th edition, and a fair question for anyone reading this in the 2020s: if a model can learn the mapping from data, why hand-design algorithms at all?

CLRS answers by describing machine learning as a way to compute when you do not know how to specify the computation. A learning method uses data to build its own procedure, which makes it the right tool when the problem is poorly understood, hard to state precisely, or drifting over time. Recognising objects in images and translating between languages are problems nobody can write a clean specification for; learning from examples is the only approach that works.

But the converse holds just as firmly. When the problem is well understood, a designed algorithm wins, and not narrowly:

Designed algorithmLearned model
Best whenThe problem has a precise specificationThe specification is unknown or shifting
CorrectnessProvable for every instanceStatistical, measured on held-out data
CostAnalysable up front, in the worst caseDepends on training and inference, empirically
Sorting a listΘ(n lg n), exactly right, alwaysNo reason to attempt this

Nobody trains a model to sort an array or find a shortest path, because we know how, and knowing how yields guarantees that a learned approximation cannot offer. The book also notes that data science — extracting knowledge from data — leans on the algorithms in this book directly, machine learning among them. The two are layered, not opposed.

Recap

The eight things to carry forward

Where this goes next

Chapter 2 makes all of this concrete. It gives insertion sort in full pseudocode and proves it correct with a loop invariant, which is the technique that discharges the universal claim in the definition of correctness. Then it introduces the analysis machinery that produced the c₁n² and c₂n lg n figures used above, and derives merge sort by divide-and-conquer along with the recurrence that describes its cost. Chapter 3 then defines O, Ω, and Θ properly, so “roughly c₁n²” becomes a statement you can prove things with.


All chapters Ch 2 — Getting Started