Huffman Codes
Huffman coding builds a provably optimal prefix-free binary code by repeatedly merging the two least frequent symbols. We develop prefix-free codes as binary trees, give the algorithm with a priority queue, build a Huffman tree from example frequencies, prove optimality with the same greedy-choice-plus-substructure argument, and pin the running time at .
╌╌╌╌
Suppose we want to store or transmit a file of text drawn from some alphabet of
symbols, letters being the obvious case. A fixed-length code assigns every
symbol a bit string of the same length: with symbols we would spend bits
each (), regardless of how often each symbol appears. But
real text is lopsided. In English, e and t are everywhere while q and z
are rare. It is wasteful to spend as many bits on z as on e.
A variable-length code exploits this skew: give the frequent symbols short codewords and the rare symbols long ones, so the total bit count drops. Huffman's 1952 algorithm finds the variable-length code that compresses a given file as much as any such code possibly can, and it does so with a simple greedy rule.1 This lesson is the payoff of the greedy method: a non-obvious algorithm, a short correctness proof, and a result used billions of times a day inside JPEG, MP3, gzip, and PNG.
Prefix-free codes
Variable-length codes carry a hazard. If e is 0 and t is 01, then the
stream 001 is ambiguous (is it e e t? e ??) because the codeword for e
is a prefix of the codeword for t. To decode unambiguously without separator
symbols, we insist on a prefix-free code (also called a prefix code): no
codeword is a prefix of any other.2
Prefix-freeness gives instant, unambiguous decoding: read bits left to right, and the moment the bits so far match a codeword, that codeword is the only possible symbol, so emit it and start fresh. Restricting to prefix-free codes costs nothing in compression: for any uniquely decodable code there is a prefix-free code at least as good, so we lose no optimality by considering only these.
Every prefix-free code corresponds to a binary tree. Symbols sit at the
leaves; the path from the root to a leaf spells its codeword, taking 0 for a
left edge and 1 for a right edge. Because symbols are only at leaves, no
codeword can be a prefix of another: a prefix would mean one symbol's leaf lies
on the path to another's, impossible when both are leaves. The depth of a
leaf is its codeword's length.
The compression problem
Let the alphabet be , and let symbol occur with frequency (its count, or its probability) in the file. In a code tree , let be the depth of 's leaf, the number of bits in its codeword. The total number of bits to encode the whole file is the cost of the tree:
Huffman's algorithm
Huffman's greedy insight is to build the tree bottom-up, starting from the
question: which two symbols belong deepest in the tree? The two least
frequent ones, since multiplying their long codewords by
small frequencies costs little.3 So make the two rarest symbols siblings at the
bottom, merge them into a single super-symbol
whose frequency is their sum, and
repeat on the smaller alphabet. Each merge fuses two nodes into one, so after
merges a single tree remains.
A min-priority queue keyed on
frequency makes the two least frequent
cheap to extract.
- 1
- 2a min-priority queue holding all symbols of , keyed on
- 3for to do
- 4allocate a new internal node
- 5rarest remaining
- 6next rarest
- 7
- 8callre-insert merged super-symbol
- 9returnlast node is the root
Each iteration removes two nodes and inserts one, shrinking the queue by one; the single survivor after rounds is the root of the finished tree. Reading the tree from the root gives every symbol's codeword.
Building a Huffman tree by hand
Take a six-symbol alphabet with these frequencies (in thousands of occurrences), the classic CLRS example:
| Symbol | a | b | c | d | e | f |
|---|---|---|---|---|---|---|
| Frequency | 45 | 13 | 12 | 16 | 9 | 5 |
We repeatedly merge the two smallest frequencies:
- Merge
fande→ node . - Merge
candb→ node . - Merge and
d→ node . - Merge and → node .
- Merge
aand → root .
Each step extracts the two smallest weights and re-inserts their sum, so the queue loses one element per round until a single root remains.
The resulting tree, with left edges labeled 0 and right edges 1, is:
Reading root-to-leaf gives the codewords:
| Symbol | a | b | c | d | e | f |
|---|---|---|---|---|---|---|
| Codeword | 0 | 101 | 100 | 111 | 1101 | 1100 |
The frequent a gets a single bit; the rare e and f get four. The cost is
thousand bits. A fixed-length -bit code would spend thousand bits, so Huffman saves about , and no prefix-free code does better.
Encoding concatenates codewords with nothing between them. The word face
becomes 1100 0 100 1101, the eleven-bit stream 11000100 1101.
Decoding runs the bits back through the tree: start at the root, turn left on 0
and right on 1, and the instant a leaf is reached emit its symbol and jump back
to the root. Prefix-freeness is what makes this unambiguous — a leaf is reached
exactly when a whole codeword has been consumed, never mid-codeword.
Why Huffman is optimal
Huffman is a greedy algorithm, so its proof follows the template from the previous lesson exactly: a greedy-choice property proved by an exchange argument, then optimal substructure to close the induction.4
The greedy choice is safe
The intuition is the exchange argument in one sentence: the rarest symbols belong deepest, so pushing them down and pulling frequent symbols up can never cost more.
The two swaps are easiest to see side by side. In any optimal tree the deepest sibling pair holds some leaves ; the globally rarest symbols may sit higher up. Exchanging with and with sends the rare symbols to the bottom and lifts the more frequent ones — and the cost only drops, because each moved-down leaf is rarer and each moved-up leaf is more frequent.
Optimal substructure
Together the two lemmas give the theorem by induction on .
Running time
The cost is dominated by the priority-queue operations. Building the initial min-heap from symbols takes . The loop runs times, and each iteration does two s and one Insert, each on a binary heap. Hence
If the frequencies arrive already sorted, two simple FIFO queues replace the heap
(one of original leaves, one of merged nodes, both kept in nondecreasing
frequency), and each extract two smallest
is , giving an
algorithm. The bound, like activity selection's, is really the cost
of getting the symbols into sorted order.
Entropy, arithmetic coding, and the whole-bit floor
Huffman coding is optimal among codes that assign each symbol a whole number of bits independently. This section places it against the theoretical floor and against the codes that go beyond it.
The entropy bound. Shannon's source coding theorem (1948) sets the floor: no uniquely decodable code can average fewer than the entropy bits per symbol, and a Huffman code always lands within one bit of it, .5 The example above illustrates the gap: its entropy is about bits per symbol while Huffman spends — essentially on the floor, because the frequencies are close to powers of . The one-bit slack becomes visible only on skewed sources.
The whole-bit floor. When a symbol's ideal codeword length is fractional — for a symbol of probability , ideally bits — Huffman must round up to a full bit, wasting the difference. Arithmetic coding (Rissanen & Langdon, 1979) sidesteps the integer-bit floor by encoding the entire message as a single fraction in , so a symbol can cost a fractional number of bits and the total approaches arbitrarily closely.6 Modern asymmetric numeral systems (Duda, 2009) match arithmetic coding's ratio at Huffman-like speed and are now used in Zstandard, LZFSE, and JPEG XL.7
Where it is still used. Huffman's simplicity, speed, and provable optimality within its class keep it embedded in DEFLATE (gzip, PNG, ZIP), JPEG, and MP3 to this day, usually as the final entropy-coding stage after a modeling transform. Two engineering variants matter in practice: canonical Huffman codes store the codebook as just the per-symbol lengths (the codewords are then reconstructed by a fixed rule), shrinking the header DEFLATE must transmit; and length-limited Huffman (the Package-Merge algorithm of Larmore & Hirschberg, 1990) caps the maximum codeword length so decode tables stay small, at a tiny cost in ratio.8 Huffman remains the textbook proof that a greedy algorithm, properly justified, can be exactly optimal, not merely a good heuristic.
Takeaways
- A prefix-free code lets a stream decode unambiguously — it is a binary tree with symbols at the leaves, codeword length = leaf depth.
- The goal is to minimize ; optimal trees are full.
- Huffman's algorithm greedily merges the two least-frequent nodes via a min-priority queue, times, building the tree bottom-up.
- Optimality follows the greedy template: an exchange argument shows the rarest symbols belong deepest (greedy choice), and optimal substructure closes the induction.
- Running time is , the cost of the heap operations, dropping to when frequencies are pre-sorted.
Footnotes
- Skiena, §5 — Data Compression: Huffman's greedy construction of the optimal variable-length code for a given file. ↩
- CLRS, Ch. 16 — Greedy Algorithms (§16.3): prefix-free codes and their representation as binary trees with symbols at the leaves. ↩
- CLRS, Ch. 16 — Greedy Algorithms (§16.3): the greedy rule of repeatedly merging the two least-frequent symbols, implemented with a min-priority queue. ↩
- Erickson, Ch. 4 — Greedy Algorithms (Huffman Codes): the optimality proof via greedy-choice exchange plus optimal substructure. ↩
- Shannon, C. E. (1948),
A mathematical theory of communication,
Bell System Technical Journal 27, 379–423 & 623–656 — entropy as the lower bound on average code length; a Huffman code satisfies . ↩ - Rissanen, J. & Langdon, G. G. (1979),
Arithmetic coding,
IBM Journal of Research and Development 23(2), 149–162 — encoding a whole message as one interval, escaping Huffman's whole-bit-per-symbol floor. ↩ - Duda, J. (2009),
Asymmetric numeral systems,
arXiv:0902.0271 — near-entropy compression at table-lookup speed, now used in Zstandard, LZFSE, and JPEG XL. ↩ - Larmore, L. L. & Hirschberg, D. S. (1990),
A fast algorithm for optimal length-limited Huffman codes,
Journal of the ACM 37(3), 464–473 — the Package-Merge algorithm building optimal Huffman codes under a maximum-length constraint. ↩
╌╌ END ╌╌