学习Huffman Coding

什么是霍夫曼编码 (Huffman Coding)

是一种用于无损数据压缩的权编码算法。由美国计算机科学家David Albert Huffman在1952年发明。

霍夫曼编码使用变长编码表泽源符号(如一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现几率的方法得到的,出现几率高的字母使用较短的编码,反之出现几率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

Huffman Coding的作用是什么?

用于数据压缩与解压。

我们知道英文和数字各占1个字节,中文占1个字符,也是就是2个字节;

  • utf-8编码中,中文字符占了3个字节,英文占1个字节;
  • utf-16编码中,中文字符占了3个字节,英文占2个字节;
  • utf-32编码中,所有字符均占4个字节;

我们再重温下字节:

字节是一种数据量的单位,一个字节等于8位(8 bit) bit,一个二进制数据0或1,是一bit。

所有的数据所占空间都可以用字节数据来衡量;例如Java中:

  • 一个字符(char)占2个字节,
  • 一个short占2个字节,
  • 一个int占4个字节,
  • 一个float占4个字节,
  • 一个long或double占8个字节。

代码的实现

Code Tree, Left Traversal has a value of 0, Right Traversal has a value of 1.

Coal: reduce the code tree,

  • step1: take the 2 chars with the lowest frequency
  • step2: make a 2 leaf node tree from them, the root node value is a sum of 2 leaves node's frequency
  • step3: take the next lowest frequency char, and add it to the tree

Let us understand the algorithm with an example.

 

package _Algorithm.HuffmanCode

import java.util.*

class HuffmanCoding {
    //recursive function to paint the huffman-code through the tree traversal
    private fun printCode(root: HuffmanNode?, s: String) {
        if (root?.left == null && root?.right == null && Character.isLetter(root?.c!!)) {
            println("${root?.c}:$s")
            return
        }

        //if we go left than add "0" to the node
        //if we go right than add "1" to the node
        printCode(root.left, s + "0")
        printCode(root.right, s + "1")
    }

    fun test() {
        val n = 6
        val charArray = charArrayOf('a', 'b', 'c', 'd', 'e', 'f')
        val charfreq = intArrayOf(5, 9, 12, 13, 16, 45)
        val priorityQueue = PriorityQueue<HuffmanNode>(n, MyComparator())

        for (i in 0 until n) {
            val node = HuffmanNode()
            node.c = charArray[i]
            node.data = charfreq[i]
            node.left = null
            node.right = null
            priorityQueue.add(node)
        }

        //create root node
        var root: HuffmanNode? = null

        while (priorityQueue.size > 1) {
            //first min extract
            val x = priorityQueue.poll()
            //second min extract
            val y = priorityQueue.poll()

            // to the sum of the frequency of the two nodes
            // assigning values to the f node.
            val f = HuffmanNode()
            f.data = x.data + y.data
            f.c = '-'
            f.left = x
            f.right = y
            //make the f node as the root
            root = f
            priorityQueue.add(f)
        }
        printCode(root, "")
    }
}

class MyComparator : Comparator<HuffmanNode> {
    override fun compare(o1: HuffmanNode?, o2: HuffmanNode?): Int {
        return o1?.data!! - o2?.data!!
    }
}

 

打印结果

f:0
c:100
d:101
a:1100
b:1101
e:111

压缩结果

从数据:
 val charArray = charArrayOf('a', 'b', 'c', 'd', 'e', 'f')
 val charfreq = intArrayOf(5, 9, 12, 13, 16, 45)

我们得出结果:
Finding number of bits without using Huffman:
Total number of characters = sum of frequencies = 100;
1byte = 8bits, so total number of bit = 100*8 = 800;

Using Huffman Encoding result is :
f:0 //code length is 1
c:100 //code length is 3
d:101
a:1100
b:1101
e:111
so total number of bits =
freq(f) * code_length(f) + freq(c) * code_length(c) + freq(d) * code_length(d) + freq(a) * code length(a) +
freq(b) * code_length(b) + freq(e) * code_length(e) =
45*1 + 12*3 + 13*3 + 5*4 + 9*4 + 16*3 = 224

Bits saved: 800-224 = 576.
 
posted @ 2020-05-03 10:46  johnny_zhao  阅读(258)  评论(0编辑  收藏  举报