霍夫曼树-原理、代码实现与应用



https://blog.csdn.net/google19890102/article/details/54848262
https://zh.wikipedia.org/wiki/%E9%9C%8D%E5%A4%AB%E6%9B%BC%E7%BC%96%E7%A0%81

0. 从霍夫曼编码讲起

霍夫曼树是为了获得霍夫曼编码而提出的。霍夫曼编码是一种无损的数据压缩编码,采用变长二进制编码的方式来对符号数据进行编码。它是通过评估符号出现的概率来设计不同符号的编码长度,出现概率高的符号采用较短的编码,出现概率较低的符号采用较长的编码。可以证明,霍夫曼编码是一种最优的二进制编码方式(前提是知道符号和符号出现的概率)。生成霍夫曼编码的数据结构被称为霍夫曼树,它是一种带权路径长度最小的二叉树,也被称为最优二叉树。

1. 二叉树一些基本概念

1.1. 路径

从一个节点到另一个节点的通路

1.2. 路径长度

路径上的边的条数

1.3. 节点的权

节点上的权重值

1.4. 节点的带权路径长度

根节点到某个节点的路径长度乘以该节点的权重值

1.5. 树的带权路径长度

所有叶子节点的带权路径长度之和

2. Huffman树的构建

  1. 统计所有字符的频率,按照从小到大的顺序排序,记排序好的集合为S。
  2. 若S中的元素个数小于2,则结束
  3. 取出S中频率最小的两个元素,记较小的元素为e1,较大的元素为e2
  4. 以e1为左孩子,e2为右孩子,它们的权重即为各自的频率。并创建一个父节点e3,e3的权重为e1和e2之和。
  5. 将e3放入到S中,重新排序
  6. 跳转到2

3. 代码实现(python)

有参考https://zhuanlan.zhihu.com/p/103908133, 在其基础上进行了优化。输入和输出都假定是字符串。
节点类:

class Node:
    def __init__(self, frequency):
        self.left = None
        self.right = None
        self.father = None
        self.frequency = frequency

    def is_left(self):
        return self.father.left == self

工具函数:

# 统计字符出现频率,生成映射表
def count_frequency(text):
    chars = []
    ret = []

    for char in text:
        if char in chars:
            continue
        else:
            chars.append(char)
            ret.append((char, text.count(char)))

    return ret

创建叶子节点:

# 创建叶子节点
def create_leaves(frequency_list):
    return [Node(frequency) for frequency in frequency_list]

创建Huffman树:

# 创建Huffman树
def create_huffman_tree(leaves):
    queue = leaves[:]
    queue.sort(key=lambda item: item.frequency,reverse=True)
    while len(queue) > 1:
        node_left = queue.pop(-1)
        node_right = queue.pop(-1)
        node_father = Node(node_left.frequency + node_right.frequency)
        node_father.left = node_left
        node_father.right = node_right
        node_left.father = node_father
        node_right.father = node_father
        len_queue=len(queue)
        if len_queue==0:
            queue.append(node_father)
            break
        if node_father.frequency>queue[0].frequency:
            queue.insert(0,node_father)
            continue
        for i in range(0,len_queue):
            if node_father.frequency<=queue[len_queue-1-i].frequency:
                queue.insert(len_queue-1-i+1,node_father)
                break
    queue[0].father = None
    return queue[0]

Huffman编码,得到{char:code}字典:

# Huffman编码,得到{char:code}字典
def huffman_encoding(leaves, root,char_frequency):
    char_to_code={}
    for char,frequency in char_frequency:
        char_to_code[char]=""
    for i in range(len(leaves)):
        leaf = leaves[i]
        while leaf != root:
            if leaf.is_left():
                char_to_code[char_frequency[i][0]] = '0' + char_to_code[char_frequency[i][0]]
            else:
                char_to_code[char_frequency[i][0]] = '1' + char_to_code[char_frequency[i][0]]
            leaf = leaf.father

    return char_to_code

编码整个字符串:

# 编码整个字符串
def encode_str(text, char_to_code):
    ret = ''
    for char in text:
        ret +=char_to_code[char]

    return ret

解码整个字符串:

# 解码整个字符串
def decode_str(huffman_str, char_to_code):
    ret = ''
    while huffman_str != '':
        i = 0
        for char,code in char_to_code.items():
            if code in huffman_str and huffman_str.index(code) == 0:
                ret += char
                huffman_str = huffman_str[len(code):]
            i += 1

    return ret

完整代码及测试用例见https://github.com/lankuohsing/DataStructureInPython/blob/main/tree/binary_tree/huffman_tree/huffman_code1.py ,欢迎给个star和follow!

4. 应用

对量化后的AI模型权重进行霍夫曼压缩编码,可以减小存储空间和网络下载时延。此时每个权重值是整型(一般为int8),可以看做一个字符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值