Skip to content

Commit 1f6313c

Browse files
committed
added huffman_coding in go/compression
1 parent f3f25f7 commit 1f6313c

1 file changed

Lines changed: 111 additions & 0 deletions

File tree

go/compression/huffman_coding.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// time complexity: O(n)
2+
// space complexity: O(n)
3+
4+
package compression
5+
6+
import "fmt"
7+
8+
// A Node of an Huffman tree, which can either be a leaf or an internal node.
9+
// Each node has a weight.
10+
// A leaf node has an associated symbol, but no children (i.e., left == right == nil).
11+
// A parent node has a left and right child and no symbol (i.e., symbol == -1).
12+
type Node struct {
13+
left *Node
14+
right *Node
15+
symbol rune
16+
weight int
17+
}
18+
19+
// A SymbolFreq is a pair of a symbol and its associated frequency.
20+
type SymbolFreq struct {
21+
Symbol rune
22+
Freq int
23+
}
24+
25+
// HuffTree returns the root Node of the Huffman tree by compressing listfreq.
26+
// The compression produces the most optimal code lengths, provided listfreq is ordered,
27+
// i.e.: listfreq[i] <= listfreq[j], whenever i < j.
28+
func HuffTree(listfreq []SymbolFreq) (*Node, error) {
29+
if len(listfreq) < 1 {
30+
return nil, fmt.Errorf("huffman coding: HuffTree : calling method with empty list of symbol-frequency pairs")
31+
}
32+
q1 := make([]Node, len(listfreq))
33+
q2 := make([]Node, 0, len(listfreq))
34+
for i, x := range listfreq { // after the loop, q1 is a slice of leaf nodes representing listfreq
35+
q1[i] = Node{left: nil, right: nil, symbol: x.Symbol, weight: x.Freq}
36+
}
37+
//loop invariant: q1, q2 are ordered by increasing weights
38+
for len(q1)+len(q2) > 1 {
39+
var node1, node2 Node
40+
node1, q1, q2 = least(q1, q2)
41+
node2, q1, q2 = least(q1, q2)
42+
node := Node{left: &node1, right: &node2,
43+
symbol: -1, weight: node1.weight + node2.weight}
44+
q2 = append(q2, node)
45+
}
46+
if len(q1) == 1 { // returns the remaining node in q1, q2
47+
return &q1[0], nil
48+
}
49+
return &q2[0], nil
50+
}
51+
52+
// least removes the node with lowest weight from q1, q2.
53+
// It returns the node with lowest weight and the slices q1, q2 after the update.
54+
func least(q1 []Node, q2 []Node) (Node, []Node, []Node) {
55+
if len(q1) == 0 {
56+
return q2[0], q1, q2[1:]
57+
}
58+
if len(q2) == 0 {
59+
return q1[0], q1[1:], q2
60+
}
61+
if q1[0].weight <= q2[0].weight {
62+
return q1[0], q1[1:], q2
63+
}
64+
return q2[0], q1, q2[1:]
65+
}
66+
67+
// HuffEncoding recursively traverses the Huffman tree pointed by node to obtain
68+
// the map codes, that associates a rune with a slice of booleans.
69+
// Each code is prefixed by prefix and left and right children are labelled with
70+
// the booleans false and true, respectively.
71+
func HuffEncoding(node *Node, prefix []bool, codes map[rune][]bool) {
72+
if node.symbol != -1 { //base case
73+
codes[node.symbol] = prefix
74+
return
75+
}
76+
// inductive step
77+
prefixLeft := make([]bool, len(prefix))
78+
copy(prefixLeft, prefix)
79+
prefixLeft = append(prefixLeft, false)
80+
HuffEncoding(node.left, prefixLeft, codes)
81+
prefixRight := make([]bool, len(prefix))
82+
copy(prefixRight, prefix)
83+
prefixRight = append(prefixRight, true)
84+
HuffEncoding(node.right, prefixRight, codes)
85+
}
86+
87+
// HuffEncode encodes the string in by applying the mapping defined by codes.
88+
func HuffEncode(codes map[rune][]bool, in string) []bool {
89+
out := make([]bool, 0)
90+
for _, s := range in {
91+
out = append(out, codes[s]...)
92+
}
93+
return out
94+
}
95+
96+
// HuffDecode recursively decodes the binary code in, by traversing the Huffman compression tree pointed by root.
97+
// current stores the current node of the traversing algorithm.
98+
// out stores the current decoded string.
99+
func HuffDecode(root, current *Node, in []bool, out string) string {
100+
if current.symbol != -1 {
101+
out += string(current.symbol)
102+
return HuffDecode(root, root, in, out)
103+
}
104+
if len(in) == 0 {
105+
return out
106+
}
107+
if in[0] {
108+
return HuffDecode(root, current.right, in[1:], out)
109+
}
110+
return HuffDecode(root, current.left, in[1:], out)
111+
}

0 commit comments

Comments
 (0)