Huffman Encoding in DSA Javascript - Time & Space Complexity
We want to understand how the time needed to build a Huffman tree changes as the number of characters grows.
How does the work increase when we have more characters to encode?
Analyze the time complexity of the following code snippet.
function buildHuffmanTree(freqMap) {
const heap = new MinHeap();
for (const char in freqMap) {
heap.insert({char, freq: freqMap[char]});
}
while (heap.size() > 1) {
const left = heap.extractMin();
const right = heap.extractMin();
const merged = {freq: left.freq + right.freq, left, right};
heap.insert(merged);
}
return heap.extractMin();
}
This code builds a Huffman tree by repeatedly combining the two smallest frequency nodes until one tree remains.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The while loop that extracts two minimum nodes and inserts their merged node back into the heap.
- How many times: This loop runs about n-1 times, where n is the number of unique characters.
Each loop step involves removing two nodes and adding one back, with heap operations taking time based on heap size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 log 10 operations |
| 100 | About 100 log 100 operations |
| 1000 | About 1000 log 1000 operations |
Pattern observation: The work grows a bit faster than just the number of characters because of the log factor from heap operations.
Time Complexity: O(n log n)
This means the time to build the Huffman tree grows a little more than linearly as the number of characters increases.
[X] Wrong: "Building the Huffman tree takes only linear time because we just combine nodes."
[OK] Correct: Each combination involves heap operations that take logarithmic time, so the total time is more than just linear.
Understanding how Huffman encoding scales helps you explain efficient data compression and priority queue use in real problems.
"What if we used a simple sorted array instead of a heap for selecting minimum nodes? How would the time complexity change?"
Practice
Solution
Step 1: Understand Huffman Encoding purpose
Huffman Encoding compresses data by using shorter codes for frequent characters and longer codes for less frequent ones.Step 2: Compare options with this purpose
Only To assign shorter codes to more frequent characters matches this goal; others describe different concepts.Final Answer:
To assign shorter codes to more frequent characters -> Option BQuick Check:
Huffman Encoding = shorter codes for frequent chars [OK]
- Thinking Huffman assigns equal length codes
- Confusing compression with encryption
- Assuming alphabetical sorting is the goal
Solution
Step 1: Identify proper node structure for Huffman tree
A node should store character, frequency, and pointers to left and right children.Step 2: Check each option
const node = { char: 'a', freq: 5, left: null, right: null }; uses an object with char, freq, left, right properties which is clear and correct. const node = [ 'a', 5, null, null ]; uses an array without labels, making it unclear. const node = function(char, freq) { this.char = char; this.freq = freq; }; is a constructor but incomplete (no left/right). const node = 'a:5'; is a string, not a node.Final Answer:
const node = { char: 'a', freq: 5, left: null, right: null }; -> Option DQuick Check:
Node = object with char, freq, left, right [OK]
- Using arrays without labels for nodes
- Using strings instead of objects
- Missing left and right child pointers
Solution
Step 1: Build Huffman tree from frequencies
Combine lowest frequencies: 5('a') and 9('b') -> node with freq 14. Then combine 12('c') and 13('d') -> node with freq 25. Finally combine 14 and 25 -> root 39.Step 2: Assign codes by traversing tree
Left edges get '0', right edges get '1'. 'a' is left child of first combined node, so code is '110'.Final Answer:
110 -> Option CQuick Check:
Lowest freq char 'a' gets code '110' [OK]
- Assigning codes without building tree
- Mixing left/right code bits
- Ignoring frequency order
function buildTree(freqMap) {
let nodes = Object.entries(freqMap).map(([char, freq]) => ({char, freq}));
while(nodes.length > 1) {
nodes.sort((a,b) => a.freq - b.freq);
let left = nodes.shift();
let right = nodes.shift();
let newNode = {char: null, freq: left.freq + right.freq, left, right};
nodes.push(newNode);
}
return nodes[0];
}
What is the bug in this code?Solution
Step 1: Analyze function behavior with empty input
If freqMap is empty, Object.entries(freqMap) is empty array, so nodes is empty. The while loop condition nodes.length > 1 is false, so function returns nodes[0], which is undefined.Step 2: Identify missing edge case handling
The function does not check for empty freqMap and returns undefined instead of a valid tree or null.Final Answer:
The function does not handle empty freqMap -> Option AQuick Check:
Empty input returns undefined, missing edge case [OK]
- Assuming nodes always have elements
- Ignoring empty input edge cases
- Thinking sorting is missing when it is present
Solution
Step 1: Build Huffman tree and assign codes
Frequencies: a=1, b=2, c=3, d=4. Combine a(1) and b(2) -> node(3). Combine node(3) and c(3) -> node(6). Combine node(6) and d(4) -> root(10). Assign codes: d=0 (1 bit), c=10 (2 bits), b=110 (3 bits), a=111 (3 bits).Step 2: Calculate total bits
Total bits = (a:1*3) + (b:2*3) + (c:3*2) + (d:4*1) = 3 + 6 + 6 + 4 = 19 bits.Final Answer:
19 bits -> Option AQuick Check:
Sum(freq * code length) = 19 bits [OK]
- Adding frequencies instead of weighted bits
- Assigning wrong code lengths
- Ignoring frequency when calculating total bits
Ready to prove it?
Pass the test and this pattern moves unknown
