Skip to content

Kotlin huffman #660

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ This file lists everyone, who contributed to this repo and wanted to show up her
- dovisutu
- Antetokounpo
- Akash Dhiman
- Pincket Robbe (Kroppeb)
112 changes: 112 additions & 0 deletions contents/huffman_encoding/code/kotlin/huffman.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import java.util.PriorityQueue

// Node type
sealed class Node(val frequency: Int): Comparable<Node> {
// So can sort nodes by frequency
override fun compareTo(other: Node) = frequency - other.frequency
}

class Leaf(freq: Int, val letter: Char): Node(freq)
class Branch(val left: Node, val right: Node): Node(left.frequency + right.frequency)


// Class to contain the decoder and encoder
class HuffmanTree (textSample: String) {
private val root: Branch
private val codeBook: MutableMap<Char, String>

init {
// Calculate frequencies
val frequencyMap = textSample.groupingBy { it }.eachCount()

// Populate the queue with leaves
val priorityQueue = PriorityQueue<Node>(frequencyMap.map{(char,freq)->
Leaf(freq, char)
})

// Join leaves
while (priorityQueue.size > 1) {
val left = priorityQueue.remove()
val right = priorityQueue.remove()
priorityQueue.add(Branch(left, right))
}

// The root node is the last remaining branch. If it's a leaf, we only had 1 unique letter
root = priorityQueue.remove() as? Branch ?: error("No support for string of one unique letter")

// Initialize the code book
codeBook = mutableMapOf()
populateCodeBook(root,"")
}

// Recursively populate a codebook with encodings from a node
private fun populateCodeBook(node: Node, code: String) {
when(node) {
// Leaf node, set this char's code
is Leaf -> codeBook[node.letter] = code

// Branch node, recursively populate the children
is Branch -> {
populateCodeBook(node.left, code + "0")
populateCodeBook(node.right, code + "1")
}
}
}

// Print the code book to stdout
fun print() {
for ((k,v) in codeBook.entries) {
println("$k: $v")
}
println()
}

// Encode a string using this tree
fun encode(source: String): String {
val encoder = StringBuilder()
for (char in source) {
encoder.append(codeBook[char])
}
return encoder.toString()
}


// Decode an encoded string
fun decode(encoded: String) = buildString {
val endNode = encoded.fold(root){current, char->
// Get the next node
val next = when(char){
'0' -> current.left
'1' -> current.right
else -> error("Found `$char` in the encoded string")
}

when(next){
is Branch -> next
is Leaf -> {
append(next.letter)
// Restart from root
root
}
}
}

// If endNode isn't root, then the last visited node wasn't a leaf and the string is therefore missing bits.
if(endNode != root)
error("Unexpected end of string")
}
}


fun main() {
val sourceText = "bibbity_bobbity"
// Create huffman tree
val huffmanTree = HuffmanTree(sourceText)
// Encode the text
val encoded = huffmanTree.encode(sourceText)
println("Encoded String: $encoded")
println("Decoded String: ${huffmanTree.decode(encoded)}")

println("CodeBook:")
huffmanTree.print()
}
2 changes: 2 additions & 0 deletions contents/huffman_encoding/huffman_encoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ Whether you use a stack or straight-up recursion also depends on the language, b
[import, lang:"asm-x64"](code/asm-x64/huffman.s)
{% sample lang="scala" %}
[import, lang:"scala"](code/scala/huffman_encoding.scala)
{% sample lang="kotlin" %}
[import, lang:"kotlin"](code/kotlin/huffman.kt)
{% endmethod %}

<script>
Expand Down