哈夫曼树(Huffman Tree)和哈夫曼编码(Huffman Coding)是一种用于数据压缩的技术,由David A. Huffman于1952年提出。哈夫曼树是一种特殊的二叉树,用于构建哈夫曼编码。哈夫曼编码是一种变长编码,用于将字符映射到不同长度的比特串,以实现数据的高效压缩。
哈夫曼树
构建哈夫曼树
哈夫曼树是一种带权路径长度最短的树,即权值越大的节点离根节点越近。
构建哈夫曼树的基本思想是:从所有权值为正整数的叶子节点开始,每次选取两个权值最小的节点合并成一个新的节点,直到最后只剩下一个根节点为止。
哈夫曼树的性质
哈夫曼树是一棵满二叉树(所有非叶子节点的度都是2)。
树中的每个叶子节点代表一个字符,其权值即为字符出现的频率。
哈夫曼树的带权路径长度(WPL)最小,即每个字符的编码长度最短。
哈夫曼编码
构建哈夫曼编码
哈夫曼编码是一种前缀编码,即没有任何一个字符的编码是另一个字符编码的前缀。
从哈夫曼树的根节点开始,沿着左子树路径走一步,编码为0;沿着右子树路径走一步,编码为1。
将所有字符根据哈夫曼树的路径映射到对应的编码。
构建哈夫曼树的基本思想和步骤:
步骤一:统计字符频率:
遍历待编码的字符串或者字符流,统计每个字符出现的频率。
步骤二:构建哈夫曼树:
将每个字符和其对应的频率作为叶子节点,构建一个包含所有叶子节点的森林。
每次从森林中选择两个权值最小的树(即叶子节点),合并成一个新的树,新树的权值为两个子树的权值之和。
不断重复上述合并步骤,直到森林中只剩下一棵树为止,这棵树即为哈夫曼树。
步骤三:生成哈夫曼编码:
根据构建好的哈夫曼树,从根节点开始,沿着左子树路径走一步编码为0,沿着右子树路径走一步编码为1,直到到达叶子节点。
将每个字符根据路径映射到对应的编码。
import heapq
from collections import defaultdict
class TreeNode:
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 build_huffman_tree(text):
# 统计字符频率
freq_map = defaultdict(int)
for char in text:
freq_map[char] += 1
# 创建优先队列(最小堆)用于构建哈夫曼树
priority_queue = [TreeNode(char, freq) for char, freq in freq_map.items()]
heapq.heapify(priority_queue)
# 构建哈夫曼树
while len(priority_queue) > 1:
left = heapq.heappop(priority_queue)
right = heapq.heappop(priority_queue)
merged_node = TreeNode(None, left.freq + right.freq)
merged_node.left = left
merged_node.right = right
heapq.heappush(priority_queue, merged_node)
# 返回哈夫曼树的根节点
return priority_queue[0]
def generate_huffman_codes(root):
def dfs(node, code, result):
if node:
if node.char:
result[node.char] = code
dfs(node.left, code + '0', result)
dfs(node.right, code + '1', result)
huffman_codes = {}
dfs(root, '', huffman_codes)
return huffman_codes
# 测试
text = "ABRACADABRA"
huffman_tree = build_huffman_tree(text)
huffman_codes = generate_huffman_codes(huffman_tree)
print("Huffman Codes:")
for char, code in huffman_codes.items():
print(f"{char}: {code}")
以上代码首先定义了TreeNode类来表示哈夫曼树的节点,然后实现了build_huffman_tree函数来构建哈夫曼树,generate_huffman_codes函数来生成哈夫曼编码。在build_huffman_tree函数中,我们使用了优先队列(最小堆)来构建哈夫曼树,通过不断合并权值最小的节点来构建整棵树。在generate_huffman_codes函数中,我们使用深度优先搜索(DFS)来遍历哈夫曼树,生成每个字符的哈夫曼编码。最后,打印出生成的哈夫曼编码。
哈夫曼编码的优势
哈夫曼编码根据字符出现的频率,将出现频率高的字符编码成短比特串,出现频率低的字符编码成长比特串,从而实现数据的高效压缩。
由于哈夫曼编码是前缀编码,所以在解码时不会出现歧义,可以根据编码直接还原出原始数据。