Huffman Tree
哈夫曼树的定义
操作定义
interface HuffmanTree {
fun isEmpty(): Boolean = size() == 0
fun size(): Int
/**
* 获取根节点
*/
fun getRoot(): HNode?
/**
* weighted path length
*/
fun wpl(): Int
/**
* 获取编码表
*/
fun getEncodingTable(): Map<Char, String>
fun printEncodingTable()
fun printTree()
}
节点定义
/**
* 哈夫曼树的节点
*/
data class HNode(
val data: Char,
val weight: Int,
val depth: Int = 0,
val left: HNode? = null,
val right: HNode? = null
) {
fun isLeaf(): Boolean = left == null && right == null
}
internal fun HNode.merge(node: HNode): HNode {
return HNode(
data = '?',
weight = this.weight + node.weight,
left = if (this.weight <= node.weight) this else node,
right = if (this.weight > node.weight) this else node
)
}
哈夫曼树的实现
internal class HuffmanTreeImpl : HuffmanTree {
private var root: HNode? = null
private var size: Int = 0
private val encodingTable: MutableMap<Char, String> = mutableMapOf()
constructor(charCountMap: Map<Char, Int>) {
this.root = this.buildTree(charCountMap)
this.buildEncodingTable(this.root)
}
override fun size() = this.size
/**
* 不针对 size=0 或者 size = 1 的进行构建
*/
private fun buildTree(charCountMap: Map<Char, Int>): HNode? {
if (charCountMap.size <= 1) {
println("Huffman Tree cannot be built with less than 2 characters.")
return null
}
this.size = charCountMap.size
val heap: Heap<HNode> = HeapImpl(HeapType.MIN_HEAP, Comparator.comparing { it.weight })
charCountMap.forEach { (c, count) ->
heap.add(HNode(data = c, weight = count))
}
while (heap.size() > 1) {
val min = heap.extract()
val secondMin = heap.extract()
heap.add(min!!.merge(secondMin!!))
}
return heap.extract()
}
override fun getRoot(): HNode? = root
override fun wpl(): Int {
return 0
}
override fun getEncodingTable(): Map<Char, String> = encodingTable
/**
* 构建编码表
*
*/
private fun buildEncodingTable(root: HNode?) {
if (root == null) {
return
}
this.dfsBuildEncodingTable(root, "0")
}
/**
* 向左为0 向右为1
*/
private fun dfsBuildEncodingTable(node: HNode, current: String) {
if (node.isLeaf()) {
encodingTable[node.data] = current
}
node.left?.let { dfsBuildEncodingTable(it, current + "0") }
node.right?.let { dfsBuildEncodingTable(it, current + "1") }
}
override fun printEncodingTable() {
println("Encoding Table:")
encodingTable.forEach { (c, s) ->
println("'$c':$s")
}
}
override fun printTree() {
val r = this.root
if (r == null) {
println("Empty Tree")
return
}
// 根节点不加连接符
println(nodeLabel(r))
// 递归打印左右子树
printTree(r.left, prefix = "", isLeft = true)
printTree(r.right, prefix = "", isLeft = false)
}
private fun printTree(node: HNode?, prefix: String, isLeft: Boolean) {
if (node == null) return
val branch = if (isLeft) "├── " else "└── "
println(prefix + branch + nodeLabel(node))
val childPrefix = prefix + if (isLeft) "│ " else " "
printTree(node.left, childPrefix, true)
printTree(node.right, childPrefix, false)
}
private fun nodeLabel(n: HNode): String {
return if (n.isLeaf()) "'${n.data}':${n.weight}" else "*:${n.weight}"
}
}
27 January 2026