Huffman Encoding creates a prefix-free binary code with minimum total encoding cost.
The tree grows from repeated two-smallest merges — internal nodes pop in as they’re created:
⚠️ Animation & Content Notice
The animation work is not fully finished — some animations may have slight errors.
If there is a major error in the content or if the animation or content is difficult to understand, please contact us at rayyancodingschool@gmail.com.
Huffman Coding
Build an optimal prefix-code tree by repeatedly merging the two smallest frequencies.
Keep all frequencies in a min-heap; repeatedly pop the two smallest, merge them into a parent whose weight is their sum, and push it back. Frequent symbols merge late and stay shallow (short codes); rare ones go deep. The greedy smallest-first merge is provably optimal.
1
heap = all leaf frequencies
2
while heap.size > 1:
3
x = pop min; y = pop min
4
node = new(x + y)
5
push(node) into heap
The main idea is simple:
Repeatedly merge the two symbols with the smallest frequencies.
Focus on recognizing:
“Frequencies” + “Prefix-free encoding” + “Minimum cost” = Huffman
Pattern Table
| Pattern | Typical Questions | Trigger |
|---|---|---|
| Build Huffman Tree | Minimum encoding cost | Merge two smallest |
| Generate Codes | Get binary codes | DFS through the tree |
| Encode String | Convert text to bits | Use generated codes |
| Decode String | Convert bits to text | Traverse the tree |
Mental Trigger
Min-Heap → Take 2 smallest → Merge → Put back → Repeat
1. Generic Huffman Template (Base)
This is the main Huffman template to memorize.
class HuffmanNode implements Comparable<HuffmanNode> {
char ch;
int freq;
HuffmanNode left;
HuffmanNode right;
HuffmanNode(char ch, int freq) {
this.ch = ch;
this.freq = freq;
}
public int compareTo(HuffmanNode other) {
return Integer.compare(this.freq, other.freq);
}
}
public HuffmanNode buildTree(char[] chars, int[] freqs) {
PriorityQueue<HuffmanNode> pq = new PriorityQueue<>();
for (int i = 0; i < chars.length; i++) {
pq.offer(new HuffmanNode(chars[i], freqs[i]));
}
while (pq.size() > 1) {
HuffmanNode left = pq.poll();
HuffmanNode right = pq.poll();
HuffmanNode merged =
new HuffmanNode('-', left.freq + right.freq);
merged.left = left;
merged.right = right;
pq.offer(merged);
}
return pq.poll();
}import heapq
class HuffmanNode:
def __init__(self, ch, freq):
self.ch = ch
self.freq = freq
self.left = None
self.right = None
def __lt__(self, other):
return self.freq < other.freq
def build_tree(chars, freqs):
pq = [HuffmanNode(chars[i], freqs[i]) for i in range(len(chars))]
heapq.heapify(pq)
while len(pq) > 1:
left = heapq.heappop(pq)
right = heapq.heappop(pq)
merged = HuffmanNode('-', left.freq + right.freq)
merged.left = left
merged.right = right
heapq.heappush(pq, merged)
return heapq.heappop(pq)struct HuffmanNode {
char ch;
int freq;
HuffmanNode* left;
HuffmanNode* right;
HuffmanNode(char c, int f)
: ch(c), freq(f), left(nullptr), right(nullptr) {}
};
struct Compare {
bool operator()(HuffmanNode* a, HuffmanNode* b) {
return a->freq > b->freq;
}
};
HuffmanNode* buildTree(vector<char>& chars, vector<int>& freqs) {
priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> pq;
for (int i = 0; i < (int)chars.size(); i++) {
pq.push(new HuffmanNode(chars[i], freqs[i]));
}
while (pq.size() > 1) {
HuffmanNode* left = pq.top(); pq.pop();
HuffmanNode* right = pq.top(); pq.pop();
HuffmanNode* merged =
new HuffmanNode('-', left->freq + right->freq);
merged->left = left;
merged->right = right;
pq.push(merged);
}
return pq.top();
}// ponytail: JS has no builtin heap; sort-per-merge keeps the snippet tiny (O(n² log n))
class HuffmanNode {
constructor(ch, freq) {
this.ch = ch;
this.freq = freq;
this.left = null;
this.right = null;
}
}
function buildTree(chars, freqs) {
const pq = chars.map((ch, i) => new HuffmanNode(ch, freqs[i]));
while (pq.length > 1) {
pq.sort((a, b) => a.freq - b.freq);
const left = pq.shift();
const right = pq.shift();
const merged = new HuffmanNode('-', left.freq + right.freq);
merged.left = left;
merged.right = right;
pq.push(merged);
}
return pq[0];
}Everything else in Huffman is built on this tree.
Pattern 1: Build Huffman Tree
What does this do?
Builds the Huffman tree by repeatedly combining the two smallest frequencies.
Example:
A = 5
B = 9
C = 12
D = 13
First:
5 + 9 = 14
Then:
12 + 13 = 25
Then continue until only one tree remains.
What Changed from the Base?
This is the base pattern itself.
The important part is:
HuffmanNode left = pq.poll();
HuffmanNode right = pq.poll();
Take the two smallest nodes.
Then:
HuffmanNode merged =
new HuffmanNode('-', left.freq + right.freq);
Create a new node containing their combined frequency.
Then:
pq.offer(merged);
Put the merged node back into the min-heap.
Huffman Tree = Min-Heap + Take 2 Smallest + Merge + Push Back
Pattern 2: Generate Huffman Codes
Once the tree is built, traverse it to generate the binary code for every character.
Java Code
public void generateCodes(
HuffmanNode root,
String code,
Map<Character, String> codes) {
if (root == null) {
return;
}
// Leaf node
if (root.left == null && root.right == null) {
codes.put(root.ch, code);
return;
}
generateCodes(root.left, code + "0", codes);
generateCodes(root.right, code + "1", codes);
}def generate_codes(root, code, codes):
if root is None:
return
# Leaf node
if root.left is None and root.right is None:
codes[root.ch] = code
return
generate_codes(root.left, code + "0", codes)
generate_codes(root.right, code + "1", codes)void generateCodes(HuffmanNode* root, string code,
unordered_map<char, string>& codes) {
if (root == nullptr) {
return;
}
// Leaf node
if (root->left == nullptr && root->right == nullptr) {
codes[root->ch] = code;
return;
}
generateCodes(root->left, code + "0", codes);
generateCodes(root->right, code + "1", codes);
}function generateCodes(root, code, codes) {
if (root === null) {
return;
}
// Leaf node
if (root.left === null && root.right === null) {
codes.set(root.ch, code);
return;
}
generateCodes(root.left, code + "0", codes);
generateCodes(root.right, code + "1", codes);
}What Changed from the Base?
1. Added recursion
Base:
// Build the tree
Changed:
generateCodes(root.left, code + "0", codes);
generateCodes(root.right, code + "1", codes);
because we need to visit every node in the Huffman tree.
2. Track the current code
Added:
String code
because every left/right decision creates one bit.
left → 0
right → 1
3. Store codes at leaf nodes
Added:
if (root.left == null && root.right == null) {
codes.put(root.ch, code);
}
because only leaf nodes represent actual characters.
Generate Codes = DFS + Left → 0 + Right → 1 + Store at leaves
Pattern 3: Encode a String
After generating the Huffman codes, replace each character with its code.
Java Code
public String encode(
String text,
Map<Character, String> codes) {
StringBuilder result = new StringBuilder();
for (char ch : text.toCharArray()) {
result.append(codes.get(ch));
}
return result.toString();
}def encode(text, codes):
result = []
for ch in text:
result.append(codes[ch])
return ''.join(result)string encode(string& text, unordered_map<char, string>& codes) {
string result;
for (char ch : text) {
result += codes[ch];
}
return result;
}function encode(text, codes) {
let result = '';
for (const ch of text) {
result += codes.get(ch);
}
return result;
}What Changed from the Base?
Added input string
Base:
// Build tree
Changed:
String text
because we now need to encode actual data.
Look up each character
Added:
codes.get(ch)
because the Huffman tree has already given every character its binary code.
Encode = Character → Look up Huffman code → Append bits
Pattern 4: Decode a Huffman String
To decode, start at the root and follow the bits.
0 → left
1 → right
When you reach a leaf, you found one character.
Java Code
public String decode(
String bits,
HuffmanNode root) {
StringBuilder result = new StringBuilder();
HuffmanNode current = root;
for (char bit : bits.toCharArray()) {
if (bit == '0') {
current = current.left;
} else {
current = current.right;
}
if (current.left == null &&
current.right == null) {
result.append(current.ch);
current = root;
}
}
return result.toString();
}def decode(bits, root):
result = []
current = root
for bit in bits:
if bit == '0':
current = current.left
else:
current = current.right
if current.left is None and current.right is None:
result.append(current.ch)
current = root
return ''.join(result)string decode(string& bits, HuffmanNode* root) {
string result;
HuffmanNode* current = root;
for (char bit : bits) {
if (bit == '0') {
current = current->left;
} else {
current = current->right;
}
if (current->left == nullptr &&
current->right == nullptr) {
result += current->ch;
current = root;
}
}
return result;
}function decode(bits, root) {
let result = '';
let current = root;
for (const bit of bits) {
if (bit === '0') {
current = current.left;
} else {
current = current.right;
}
if (current.left === null && current.right === null) {
result += current.ch;
current = root;
}
}
return result;
}What Changed from the Base?
1. Traverse the tree using bits
Added:
if (bit == '0') {
current = current.left;
} else {
current = current.right;
}
because each bit tells us which direction to move.
2. Detect a leaf
Added:
if (current.left == null &&
current.right == null)
because reaching a leaf means one character has been decoded.
3. Return to root
Added:
current = root;
because the next bits represent the next character.
Decode = Bit → Traverse tree → Reach leaf → Output character → Return to root
Huffman Pattern Evolution
Build Tree
↓
Min-Heap
↓
Take 2 smallest
↓
Merge
↓
Push back
↓
Tree complete
↓
DFS
↓
Generate 0/1 codes
↓
Encode / Decode
Common Mistakes
1. Using a normal queue
Wrong:
Queue<HuffmanNode> queue;
Use:
PriorityQueue<HuffmanNode> pq;
because we always need the two smallest frequencies.
2. Using a max-heap
Huffman needs:
smallest frequency first
So use a min-heap.
3. Forgetting the leaf check
Wrong:
codes.put(root.ch, code);
Not every node represents a character.
Correct:
if (root.left == null && root.right == null) {
codes.put(root.ch, code);
}
4. Mixing up left and right
Use a consistent rule:
left → 0
right → 1
5. Forgetting to put the merged node back
After:
left + right
you must:
pq.offer(merged);
Otherwise the algorithm cannot continue.
Recognition Cheat Sheet
| If you see… | Think… |
|---|---|
| Frequencies + encoding | Huffman |
| Prefix-free codes | Huffman |
| Minimum encoding cost | Huffman |
| Merge two smallest | Min-Heap + Greedy |
| Generate binary codes | DFS on Huffman tree |
| Decode 0/1 string | Traverse Huffman tree |
Complexity
For n unique characters:
Build Tree:
O(n log n)
Generate Codes:
O(n)
Space:
O(n)
Simple Mental Model
Take the 2 smallest → Merge them → Repeat → DFS the tree for codes.
Premium Content
Unlock Huffman Encoding and all premium lessons with a subscription.
From ₹199.99/year — See plans