Post

Algorithms + Data Structures = Programs

Algorithms + Data Structures = Programs

A review and critical analysis of Niklaus Wirth’s foundational text

Over the past decade, I have been thinking about how programming languages evolve and what survives the churn. Languages rise and fall in popularity. Frameworks come and go. But certain ideas persist across every generation of tooling. With the recent rise of AI coding assistants, developers are increasingly exposed to a variety of programming languages in a single workday. An LLM will happily generate Python, Rust, Go, or CUDA for you. It does not care about your language allegiance. This polyglot reality raises a question: what are the essential programming constructs every developer should internalize, independent of any particular language?

There is no better place to start than Niklaus Wirth’s Algorithms + Data Structures = Programs1, first published in 1976 and revised in subsequent editions. The title itself is a thesis statement. A program is not a sequence of clever tricks. It is the composition of well-chosen data representations with algorithms that operate on them. Wirth’s argument is that the two are inseparable and must be designed together.

The Book

Wirth structures the book around a progression from simple to complex, in both data and control:

  1. Fundamental Data Structures. Arrays, records, sets, and sequences (files). Wirth introduces these not as language features but as concepts with concrete machine representations. He discusses how arrays map to contiguous memory, how records compose heterogeneous fields, and how sets can be represented as bit vectors. The distinction between the abstract concept and its physical layout is made explicit from the start.

  2. Sorting. Straight insertion, selection, exchange, then the advanced methods: Shell sort, heapsort (tree sort), quicksort (partition sort). Wirth does not just present the algorithms. He analyzes them — counting comparisons and moves, discussing best-case and worst-case behavior, and comparing them empirically. The chapter on sorting sequences (external sorting via merging) is particularly valuable because most modern curricula skip it entirely.

  3. Recursive Algorithms. Recursion is treated as a first-class algorithmic technique, not merely a language feature. Wirth walks through recursive data definitions (trees, expressions), recursive algorithms (quicksort, the eight queens problem, the knight’s tour), and backtracking. He connects recursion to the call stack, making the cost model explicit.

  4. Dynamic Data Structures. Linked lists, trees (including balanced trees — AVL trees and B-trees), and multiway structures. This is where the book becomes genuinely demanding. Wirth implements balanced tree insertion and deletion with full rotational logic. The B-tree chapter is one of the clearest treatments of the topic from that era.

  5. Key Transformations (Hashing). Hash tables, collision resolution strategies, and the tradeoffs between chaining and open addressing.

  6. Language Structures and Compilers. The final chapter is a surprise for a “data structures” textbook. Wirth devotes it to formal language definition (BNF grammars), syntax analysis (recursive descent parsing), and code generation. This reflects his deeply held belief that understanding how languages are implemented is part of understanding how to program.

The original 1976 edition used Pascal. Later editions were rewritten in Modula-2, and the 2004 revision uses Oberon2. Wirth’s own progression of languages mirrors his philosophy: each successive language is a refinement, stripping away unnecessary complexity.

What Holds Up

Several things about this book remain strikingly relevant:

The inseparability of data and algorithms. This sounds obvious, but in practice, many developers still design their data models in isolation and then struggle to write efficient algorithms over them. Wirth’s central thesis — choose the data representation first, and the algorithm often follows naturally — is a discipline that modern “move fast” culture tends to skip.

Explicit cost models. Wirth counts comparisons. He counts memory accesses. He draws tables of empirical measurements. In an era where developers invoke library functions without understanding their complexity, this pedagogical approach is valuable. Knowing that quicksort’s average case is $O(n \log n)$ is useful; understanding why it degrades to $O(n^2)$ on pre-sorted input is essential.

The compiler chapter. Treating language implementation as a core part of computer science education is increasingly rare. But if you are going to work with AI tools that generate code across multiple languages, having a mental model of how languages are parsed, type-checked, and compiled gives you a significant edge in debugging generated code that looks correct but is subtly wrong.

Representation matters. Wirth consistently shows how the same abstract data type can have wildly different performance characteristics depending on its physical representation. An array-of-structs vs. a struct-of-arrays. A linked list vs. a contiguous buffer. A hash table with chaining vs. open addressing. These choices do not show up in type signatures or API documentation. They show up in cache miss rates and memory allocation patterns.

What Shows Its Age

The book is not without limitations when read today:

Concurrency is absent. The book was written for a world of sequential, single-core computation. There is no discussion of locks, atomics, message passing, or parallel algorithms. This is perhaps the single largest gap for a modern reader. Any developer working on systems today must understand concurrent data structures, and Wirth’s book offers no guidance here.

No discussion of memory hierarchy. Wirth’s cost model counts comparisons and pointer dereferences, but it does not account for cache lines, TLB misses, or NUMA topology. On modern hardware, a linked list traversal and an array scan of the same logical data can differ by an order of magnitude in wall-clock time purely due to memory access patterns. The algorithms in this book are analyzed in a flat-memory model that no longer reflects reality.

The language choice. Pascal, Modula-2, and Oberon are all Wirth’s own languages. They are clean, well-designed, and pedagogically effective. They are also not what anyone ships production code in today. A reader must mentally translate the idioms. This is not a fatal flaw (the algorithms are language-agnostic) but it does add friction, especially for readers in the current era.

The scope is narrow by modern standards. There is no discussion of graphs (beyond trees), no probabilistic data structures (bloom filters, count-min sketches, HyperLogLog), no persistent or functional data structures, and no treatment of compression or serialization. These are topics that a modern systems programmer encounters daily.

No treatment of testing or correctness. Wirth asserts correctness through careful construction. There is no discussion of invariants, formal verification, property-based testing, or even unit testing. The discipline of proving that a data structure maintains its invariants under all operations is left largely to the reader’s diligence.

Why It Matters Now

The paradox of AI-assisted programming is that it simultaneously lowers the barrier to writing code and raises the bar for understanding it. When an LLM generates a red-black tree implementation or a merge sort in a language you have never used, you need a mental framework to evaluate whether it is correct, efficient, and appropriate for the task. You need to know what questions to ask: Is this $O(n \log n)$ or $O(n^2)$? Does it allocate on the heap or the stack? What happens under contention?

Wirth’s book provides that framework. Not because it covers every modern topic but because it teaches a way of thinking. Data and algorithms are not separate concerns. Representation determines performance as well as developer experience. Abstraction has a cost. The machine is not infinitely fast, memory is not infinitely cheap, and developers attention span continues to decline.

The title is the lesson: Algorithms + Data Structures = Programs. Not frameworks. Not libraries. Not language features. Algorithms and data structures. If you internalize this, you can work in any language an LLM generates for you. If you do not, you are at the mercy of whatever the model hallucinated.

For the systems engineers and compiler developers reading this: you already know this. But the next generation of developers — the ones who will grow up writing prompts instead of for-loops — may not. Recommending Wirth to them is not nostalgia. It is pragmatism.

The full PDF of the 2004 Oberon edition is available from ETH Zurich2.

References

Disclaimer: This article was generated using the Gemini 3.1 Pro and Claude Opus 4.8 models.

  1. Algorithms + Data Structures = Programs: Niklaus Wirth, Prentice-Hall, 1976. (Wikipedia↩︎

  2. Algorithms and Data Structures (2004 Oberon Edition): Niklaus Wirth, ETH Zurich. Free PDF. (Link↩︎ ↩︎2

This post is licensed under CC BY 4.0 by the author.