Bird
Raised Fist0
DSA Goprogramming

Huffman Encoding in DSA Go

Choose your learning style10 modes available
Mental Model
Huffman encoding builds a tree to assign shorter codes to frequent letters and longer codes to rare letters, saving space.
Analogy: Imagine packing a suitcase: you put small, often-used items in easy-to-reach spots and big, rarely-used items deeper inside to save space.
Frequency table:
A:5 B:9 C:12 D:13 E:16 F:45

Initial nodes:
[A:5] [B:9] [C:12] [D:13] [E:16] [F:45]

Tree after first merge:
      [14]
     /    \
  [A:5]  [B:9]

Remaining nodes:
[14] [C:12] [D:13] [E:16] [F:45]

↑ shows current smallest nodes merged
Dry Run Walkthrough
Input: Characters with frequencies: A=5, B=9, C=12, D=13, E=16, F=45
Goal: Build a Huffman tree and assign binary codes to each character minimizing total bits
Step 1: Pick two smallest frequencies A(5) and B(9), merge into node with freq 14
[14] -> [C:12] -> [D:13] -> [E:16] -> [F:45]
Why: Combine smallest to build tree bottom-up, ensuring minimal code length for frequent chars
Step 2: Pick two smallest nodes C(12) and D(13), merge into node with freq 25
[14] -> [25] -> [E:16] -> [F:45]
Why: Continue merging smallest nodes to build tree
Step 3: Pick two smallest nodes 14 and E(16), merge into node with freq 30
[25] -> [30] -> [F:45]
Why: Keep merging to build higher tree levels
Step 4: Pick two smallest nodes 25 and 30, merge into node with freq 55
[45] -> [55]
Why: Merge to approach root node
Step 5: Merge last two nodes 45 and 55 into root node with freq 100
[100]
Why: Final root node represents total frequency
Step 6: Assign binary codes by traversing tree: left edge=0, right edge=1
Codes: F=0, C=100, D=101, A=1100, B=1101, E=111
Why: Assign codes so frequent chars have shorter codes
Result:
Huffman tree root with freq 100
Codes:
F:0
C:100
D:101
A:1100
B:1101
E:111
Annotated Code
DSA Go
package main

import (
	"container/heap"
	"fmt"
)

// Node represents a tree node for Huffman encoding
// It holds frequency, character, and left/right children
type Node struct {
	char  rune
	freq  int
	left  *Node
	right *Node
}

// PriorityQueue implements heap.Interface for Nodes
// It orders nodes by frequency ascending
type PriorityQueue []*Node

func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool { return pq[i].freq < pq[j].freq }
func (pq PriorityQueue) Swap(i, j int) { pq[i], pq[j] = pq[j], pq[i] }
func (pq *PriorityQueue) Push(x interface{}) { *pq = append(*pq, x.(*Node)) }
func (pq *PriorityQueue) Pop() interface{} {
	n := len(*pq)
	item := (*pq)[n-1]
	*pq = (*pq)[:n-1]
	return item
}

// buildHuffmanTree builds the Huffman tree from frequency map
func buildHuffmanTree(freqMap map[rune]int) *Node {
	pq := &PriorityQueue{}
	heap.Init(pq)

	// Push all chars as leaf nodes
	for ch, freq := range freqMap {
		heap.Push(pq, &Node{char: ch, freq: freq})
	}

	// Merge nodes until one tree remains
	for pq.Len() > 1 {
		left := heap.Pop(pq).(*Node) // smallest freq node
		right := heap.Pop(pq).(*Node) // second smallest

		// Create new internal node with combined freq
		merged := &Node{freq: left.freq + right.freq, left: left, right: right}

		heap.Push(pq, merged) // push merged node back
	}

	return heap.Pop(pq).(*Node) // root node
}

// generateCodes traverses tree to assign binary codes
func generateCodes(node *Node, prefix string, codes map[rune]string) {
	if node == nil {
		return
	}

	// Leaf node: assign code
	if node.left == nil && node.right == nil {
		codes[node.char] = prefix
		return
	}

	// Traverse left with '0'
	generateCodes(node.left, prefix+"0", codes)
	// Traverse right with '1'
	generateCodes(node.right, prefix+"1", codes)
}

func main() {
	freqMap := map[rune]int{'A': 5, 'B': 9, 'C': 12, 'D': 13, 'E': 16, 'F': 45}

	root := buildHuffmanTree(freqMap)

	codes := make(map[rune]string)
	generateCodes(root, "", codes)

	for ch, code := range codes {
		fmt.Printf("%c: %s\n", ch, code)
	}
}
left := heap.Pop(pq).(*Node) // smallest freq node
extract smallest frequency node to merge
right := heap.Pop(pq).(*Node) // second smallest
extract second smallest frequency node
merged := &Node{freq: left.freq + right.freq, left: left, right: right}
create new internal node combining two smallest nodes
heap.Push(pq, merged) // push merged node back
insert merged node back into priority queue
if node.left == nil && node.right == nil { codes[node.char] = prefix; return }
assign code to leaf node representing a character
generateCodes(node.left, prefix+"0", codes)
traverse left subtree adding '0' to code prefix
generateCodes(node.right, prefix+"1", codes)
traverse right subtree adding '1' to code prefix
OutputSuccess
F: 0 C: 100 D: 101 A: 1100 B: 1101 E: 111
Complexity Analysis
Time: O(n log n) because we insert and extract nodes from a priority queue n times
Space: O(n) for storing nodes and codes where n is number of unique characters
vs Alternative: Naive fixed-length codes use O(n) bits per char, Huffman reduces average bits by assigning variable-length codes based on frequency
Edge Cases
Empty frequency map
No tree is built, generateCodes does nothing
DSA Go
for pq.Len() > 1 { ... } loop skips because pq is empty
Single character frequency
Tree is a single node, code assigned is empty string
DSA Go
generateCodes assigns prefix "" to leaf node
All characters have same frequency
Tree still built, codes assigned but lengths may be similar
DSA Go
heap ordering handles equal frequencies correctly
When to Use This Pattern
When you need to compress data by assigning shorter codes to frequent items, reach for Huffman encoding because it builds an optimal prefix code tree.
Common Mistakes
Mistake: Not using a priority queue to always merge smallest frequencies first
Fix: Use a min-heap to pick two smallest nodes each time to ensure optimal tree
Mistake: Assigning codes only by node depth without considering left/right edges
Fix: Assign '0' for left edges and '1' for right edges during tree traversal
Summary
Builds a tree to assign shorter binary codes to frequent characters for compression.
Use when you want to compress data efficiently by minimizing average code length.
Always merge two smallest frequency nodes first and assign codes by tree path.

Practice

(1/5)
1. What is the main goal of Huffman Encoding in data compression?
easy
A. To sort characters alphabetically
B. To assign equal length codes to all characters
C. To encrypt data for security
D. To assign shorter codes to more frequent characters

Solution

  1. Step 1: Understand Huffman Encoding purpose

    Huffman Encoding compresses data by using variable-length codes based on character frequency.
  2. Step 2: Identify code assignment rule

    More frequent characters get shorter codes to reduce overall data size.
  3. Final Answer:

    To assign shorter codes to more frequent characters -> Option D
  4. Quick Check:

    Huffman Encoding = shorter codes for frequent chars [OK]
Hint: Remember: frequent chars get shorter codes [OK]
Common Mistakes:
  • Thinking all codes have same length
  • Confusing compression with encryption
  • Assuming alphabetical order affects codes
2. Which of the following Go code snippets correctly declares a priority queue for Huffman nodes using container/heap?
easy
A. type HuffmanNode struct {char string; freq float64}; var pq []HuffmanNode
B. var pq []int = make([]int, 0)
C. type HuffmanNode struct {char rune; freq int}; type PriorityQueue []*HuffmanNode
D. var pq map[rune]int = make(map[rune]int)

Solution

  1. Step 1: Identify correct struct for Huffman node

    HuffmanNode should have a character (rune) and frequency (int).
  2. Step 2: Check priority queue declaration

    PriorityQueue should be a slice of pointers to HuffmanNode for heap operations.
  3. Final Answer:

    type HuffmanNode struct {char rune; freq int}; type PriorityQueue []*HuffmanNode -> Option C
  4. Quick Check:

    Priority queue = slice of HuffmanNode pointers [OK]
Hint: Use slice of pointers for priority queue [OK]
Common Mistakes:
  • Using map instead of slice for priority queue
  • Using wrong data types for char or freq
  • Declaring slice of values instead of pointers
3. Given the frequencies: {'a': 5, 'b': 9, 'c': 12, 'd': 13}, what is the Huffman code for 'a' after building the tree?
medium
A. 10
B. 00
C. 111
D. 110

Solution

  1. Step 1: Build Huffman tree from frequencies

    Combine lowest frequencies: 5('a') + 9('b') = 14, then combine 12('c') + 13('d') = 25, then combine 14 + 25 = 39.
  2. Step 2: Assign codes from tree

    'a' is in the left subtree of the first combined node, so it gets code '10' (left-right).
  3. Final Answer:

    10 -> Option A
  4. Quick Check:

    'a' = code '10' [OK]
Hint: Lowest freq chars get codes starting with 1 or 0 depending on tree [OK]
Common Mistakes:
  • Assigning longer codes to lowest freq chars
  • Mixing up left and right branches
  • Ignoring tree structure when assigning codes
4. Identify the error in this Go snippet for building a Huffman tree:
func buildTree(freq map[rune]int) *Node {
  pq := &PriorityQueue{}
  heap.Init(pq)
  for c, f := range freq {
    heap.Push(pq, &Node{char: c, freq: f})
  }
  for pq.Len() > 1 {
    left := heap.Pop(pq).(*Node)
    right := heap.Pop(pq).(*Node)
    merged := &Node{freq: left.freq + right.freq, left: left, right: right}
    heap.Push(pq, merged)
  }
  return heap.Pop(pq).(*Node)
}
medium
A. PriorityQueue must implement heap.Interface methods
B. freq map keys should be string, not rune
C. heap.Init should be called after pushing elements
D. Cannot push pointers to heap

Solution

  1. Step 1: Check PriorityQueue usage with heap

    Go's heap requires the PriorityQueue type to implement heap.Interface methods Len, Less, Swap, Push, and Pop.
  2. Step 2: Identify missing implementation

    If PriorityQueue does not implement these methods, heap.Init and heap.Push will cause runtime errors.
  3. Final Answer:

    PriorityQueue must implement heap.Interface methods -> Option A
  4. Quick Check:

    heap.Interface implementation required [OK]
Hint: Always implement heap.Interface for priority queues [OK]
Common Mistakes:
  • Using wrong key type in freq map
  • Calling heap.Init too early or late
  • Trying to push non-pointer values
5. You have a string "aaabbc" and want to encode it using Huffman Encoding. Which of the following steps correctly describes how to build the Huffman tree and generate codes?
hard
A. Sort characters alphabetically, assign fixed-length codes, encode string
B. Count frequencies, build min-heap, merge nodes until one tree, assign codes from tree
C. Assign random codes to characters, then compress string
D. Use frequency to assign codes but skip building tree

Solution

  1. Step 1: Count character frequencies

    For "aaabbc", frequencies are: 'a':3, 'b':2, 'c':1.
  2. Step 2: Build min-heap and merge nodes

    Create nodes for each char, push into min-heap, merge two smallest nodes repeatedly until one tree remains.
  3. Step 3: Assign codes from the tree

    Traverse tree assigning '0' for left and '1' for right edges to generate prefix codes.
  4. Final Answer:

    Count frequencies, build min-heap, merge nodes until one tree, assign codes from tree -> Option B
  5. Quick Check:

    Huffman = freq count + min-heap + merge + code assign [OK]
Hint: Follow frequency -> min-heap -> merge -> code assign steps [OK]
Common Mistakes:
  • Skipping tree building step
  • Assigning fixed-length codes ignoring frequency
  • Using random codes without structure
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard