Skip to content

Commit 20576b6

Browse files
Julianzsparal
Julian
authored andcommitted
add huffman encoding in go (#403)
* add huffman encoding in go * change the name golang to go * change the hex representation to the char * switch the sort with a heap implementation
1 parent 473c48a commit 20576b6

File tree

2 files changed

+145
-0
lines changed

2 files changed

+145
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package main
2+
3+
import (
4+
"container/heap"
5+
"fmt"
6+
)
7+
8+
type node struct {
9+
freq int
10+
char rune
11+
left *node
12+
right *node
13+
}
14+
15+
type codebook map[rune]string
16+
type nodeHeap []*node
17+
18+
func (n nodeHeap) Len() int { return len(n) }
19+
func (n nodeHeap) Less(i, j int) bool { return n[i].freq > n[j].freq }
20+
func (n nodeHeap) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
21+
22+
func (n *nodeHeap) Push(x interface{}) {
23+
if node, ok := x.(*node); ok {
24+
*n = append(*n, node)
25+
} else {
26+
fmt.Printf("I got a node of Type %T\n", x)
27+
}
28+
}
29+
30+
func (n *nodeHeap) Pop() interface{} {
31+
old := *n
32+
l := len(old)
33+
x := old[l-1]
34+
*n = old[0 : l-1]
35+
return x
36+
}
37+
38+
func buildTree(message string) *node {
39+
freqMap := make(map[rune]*node)
40+
h := new(nodeHeap)
41+
heap.Init(h) // really needed?
42+
43+
for _, char := range message {
44+
if _, ok := freqMap[char]; ok {
45+
freqMap[char].freq++
46+
} else {
47+
newNode := new(node)
48+
newNode.freq = 1
49+
newNode.char = char
50+
freqMap[char] = newNode
51+
heap.Push(h, newNode)
52+
}
53+
}
54+
55+
for h.Len() > 1 {
56+
left, right := h.Pop().(*node), h.Pop().(*node)
57+
branch := new(node)
58+
branch.freq = right.freq + left.freq
59+
branch.left = left
60+
branch.right = right
61+
heap.Push(h, branch)
62+
}
63+
64+
root := heap.Pop(h).(*node)
65+
return root
66+
}
67+
68+
func codebookRecurse(node *node, cb *codebook, code []rune) {
69+
if node == nil {
70+
return
71+
}
72+
73+
if node.left == nil && node.right == nil {
74+
(*cb)[node.char] = string(code)
75+
}
76+
77+
code = append(code, '0')
78+
codebookRecurse(node.left, cb, code)
79+
code = append(code[:len(code)-1], '1')
80+
codebookRecurse(node.right, cb, code)
81+
}
82+
83+
func encode(message string) (string, *node, codebook) {
84+
ret := ""
85+
root := buildTree(message)
86+
cb := generateCodebook(root)
87+
for _, char := range message {
88+
ret += cb[char]
89+
}
90+
91+
return ret, root, cb
92+
}
93+
94+
func decode(message string, root *node) string {
95+
cur := root
96+
ret := ""
97+
98+
for _, char := range message {
99+
if cur == nil {
100+
return message
101+
}
102+
103+
switch string(char) {
104+
case "0":
105+
if cur.left == nil {
106+
ret += string(cur.char)
107+
cur = root.left
108+
} else {
109+
cur = cur.left
110+
}
111+
case "1":
112+
if cur.right == nil {
113+
ret += string(cur.char)
114+
cur = root.right
115+
} else {
116+
cur = cur.right
117+
}
118+
}
119+
}
120+
121+
if cur.char != 0 {
122+
ret += string(cur.char)
123+
}
124+
125+
return ret
126+
}
127+
128+
func generateCodebook(root *node) codebook {
129+
cb := make(codebook)
130+
codeArr := make([]rune, 0)
131+
codebookRecurse(root, &cb, codeArr)
132+
return cb
133+
}
134+
135+
func main() {
136+
enc, root, cb := encode("bibbity_bobbity")
137+
fmt.Println("Codebook:")
138+
for r, c := range cb {
139+
fmt.Println(string(r), "->", c)
140+
}
141+
fmt.Println("\nEncoded:", enc)
142+
fmt.Println("Decoded:", decode(enc, root))
143+
}

contents/huffman_encoding/huffman_encoding.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ Whether you use a stack or straight-up recursion also depends on the language, b
7777
[import, lang:"javascript"](code/javascript/huffman.js)
7878
{% sample lang="java" %}
7979
[import, lang:"java"](code/java/huffman.java)
80+
{% sample lang="go" %}
81+
[import, lang:"go"](code/golang/huffman.go)
8082
{% sample lang="asm-x64" %}
8183
[import, lang:"asm-x64"](code/asm-x64/huffman.s)
8284
{% endmethod %}

0 commit comments

Comments
 (0)