Python 实现哈夫曼编码:原理及代码解析
Python 实现哈夫曼编码:原理及代码解析
哈夫曼编码是一种常用的数据压缩方法,它利用字符出现的频率来构建一种变长编码,使出现频率高的字符使用较短的编码,出现频率低的字符使用较长的编码,从而实现数据压缩。
代码实现
import heapq
from collections import defaultdict
class HuffmanNode:
def __init__(self, char, freq):
self.char = char
self.freq = freq
self.left = None
self.right = None
# 哈夫曼树节点对象之间的比较规则,用于堆的排序
def __lt__(self, other):
return self.freq < other.freq
# 计算字符出现的概率
def get_char_frequency(text):
freq_dict = defaultdict(int) #创建一个默认值为 0 的空字典,如果字典中访问一个不存在的键,那么这个键的默认值就会是 0。
for char in text:
freq_dict[char] += 1 #把字符导入到字典中去并且相同的就加1
n = len(text)
for char in freq_dict:
freq_dict[char] /= n
return freq_dict
# 构建哈夫曼树
def build_huffman_tree(freq_dict):
# 将每个字符及其概率构造为一个哈夫曼树节点
heap = [HuffmanNode(char, freq) for char, freq in freq_dict.items()] #这些对象存储到一个列表中,即 heap 中。在这个列表中,每个 HuffmanNode 对象都代表了一个字符以及其出现的频率
# 用堆来存储所有哈夫曼树节点,每次取出频率最小的两个节点合并
heapq.heapify(heap) #将heap转化为堆
while len(heap) > 1:
# 取出频率最小的两个节点,合并为一个新节点
node1 = heapq.heappop(heap) #heapq.heappop弹出最小值
node2 = heapq.heappop(heap)
parent_freq = node1.freq + node2.freq
parent = HuffmanNode(None, parent_freq) #给中间的点的值都设置为空
parent.left = node1
parent.right = node2
# 将新节点放回堆中
heapq.heappush(heap, parent)
# 堆中仅剩下一个节点,即为哈夫曼树的根节点
return heap[0]
# 递归生成哈夫曼编码
def generate_huffman_code(node, code_dict, code=''):
if node.char is not None:
# 到达叶节点,将字符和对应的编码存储到字典中
code_dict[node.char] = code
return
# 递归遍历左子树和右子树
generate_huffman_code(node.left, code_dict, code + '0')
generate_huffman_code(node.right, code_dict, code + '1')
# 将文本编码为哈夫曼编码
def encode_text(text, code_dict):
return ''.join(code_dict[char] for char in text)
# 将哈夫曼编码解码为原始文本
def decode_text(encoded_text, root):
decoded_text = ''
node = root
for bit in encoded_text:
if bit == '0':
node = node.left
elif bit == '1':
node = node.right
if node.char is not None: #中间点的值都为空
decoded_text += node.char
node = root #继续开始找下一个原码
return decoded_text
text= input('输入即将被编码的字符:')
freq_dict = get_char_frequency(text)
root = build_huffman_tree(freq_dict)
code_dict = {}
generate_huffman_code(root, code_dict)
#打印每个字符的概率和对应的编码
for char, freq in freq_dict.items():
code = code_dict[char]
print(f'char: {char}, freq: {freq:.3f}, code: {code}')
#将文本编码为哈夫曼编码
encoded_text = encode_text(text, code_dict)
print(f'Encoded text: {encoded_text}')
#将哈夫曼编码解码为原始文本
decoded_text = decode_text(encoded_text, root)
print(f'Decoded text: {decoded_text}')
## 代码解释
1. **`HuffmanNode` 类:** 该类定义了哈夫曼树节点,包含字符 `char`、频率 `freq` 和左右子节点 `left` 和 `right`。
2. **`__lt__` 方法:** 该方法定义了哈夫曼树节点之间的比较规则,按照频率 `freq` 进行比较。这使得我们可以使用 Python 的内置堆排序功能 `heapq` 来实现哈夫曼树的构建。
3. **`get_char_frequency` 函数:** 该函数计算文本中每个字符出现的频率,并返回一个字典,键为字符,值为频率。
4. **`build_huffman_tree` 函数:** 该函数构建哈夫曼树。它首先将每个字符及其频率转换为 `HuffmanNode` 对象,并将其存储到一个列表 `heap` 中。然后,使用 `heapq.heapify` 将列表转化为堆,并不断从堆中弹出频率最小的两个节点,合并为一个新节点,并将新节点放回堆中。最后,堆中只剩下一个节点,即为哈夫曼树的根节点。
5. **`generate_huffman_code` 函数:** 该函数递归地遍历哈夫曼树,生成每个字符的编码。它将从根节点开始,沿着左子树遍历时添加 '0',沿着右子树遍历时添加 '1',直到到达叶节点,此时将字符和对应的编码存储到字典中。
6. **`encode_text` 函数:** 该函数将文本编码为哈夫曼编码,它根据 `code_dict` 中的字符编码,将文本中的每个字符替换为对应的编码,并将所有编码拼接起来。
7. **`decode_text` 函数:** 该函数将哈夫曼编码解码为原始文本。它从哈夫曼树的根节点开始,根据编码中的 '0' 或 '1',沿着左子树或右子树遍历,直到到达叶节点,此时将叶节点的字符添加到解码文本中。然后回到根节点继续进行下一个编码的解码。
## 代码中的堆排序
在代码中,`heapq.heappop(heap)` 用于从堆中弹出最小值。**堆排序的依据是 HuffmanNode 类中定义的 `__lt__` 方法**,该方法规定了对象之间的比较规则,即按照频率 `freq` 来比较大小。因此,`heapq.heappop(heap)` 会弹出频率最小的节点。
## 总结
本文介绍了哈夫曼编码的原理,并提供了 Python 代码实现,包含构建哈夫曼树、生成编码、编码文本和解码文本等功能。同时对代码中使用堆排序弹出最小值的机制进行了深入解释。哈夫曼编码是一种简单而高效的数据压缩方法,在实际应用中具有广泛的应用价值。
原文地址: https://www.cveoy.top/t/topic/lLMA 著作权归作者所有。请勿转载和采集!