哈夫曼树概念:
哈夫曼树(Huffman Tree),也被称为最优二叉树(Optimal binary tree),是根节点到叶子节点路径和最小的二叉树。是一种用于数据压缩的树形结构。它利用字符出现频率来构建一种编码方式,使得频率高的字符获得较短的编码,频率低的字符获得较长的编码。
概念和特点:
-
最优性:哈夫曼树是一棵最优二叉树,其具有最小的平均编码长度。换句话说,它能够以最小的位数编码来表示数据。
-
前缀编码:哈夫曼树的编码是一种前缀编码(Prefix Code)。这意味着没有一个字符的编码是另一个字符编码的前缀,使得编码的解码过程是唯一的。
-
字符频率:构建哈夫曼树的关键在于字符的频率信息。频率高的字符在哈夫曼树中对应的路径较短,频率低的字符对应的路径较长。
-
唯一性:对于给定的字符频率,哈夫曼树是唯一的。不同的字符频率会产生不同的哈夫曼树。
哈夫曼树的构建步骤:
-
统计字符频率:根据给定的数据,统计每个字符出现的频率。
-
构建优先队列:将每个字符及其频率封装成节点,并使用优先队列(如最小堆)来按照频率排序。
-
构建哈夫曼树:将优先队列中频率最低的两个节点进行合并,生成一个新的内部节点,并将其插入回优先队列。重复此过程,直到只剩下一个节点,即哈夫曼树的根节点。
-
生成编码:通过遍历哈夫曼树,沿着左子树走为0,右子树走为1,获得每个字符的编码。
-
数据压缩与解压缩:利用生成的编码,将原始数据压缩成哈夫曼编码表示的二进制数据。在解压缩时,根据哈夫曼编码和哈夫曼树进行解码,还原原始数据。
哈夫曼树的思想被广泛应用于数据压缩、通信传输、文件存储等领域。通过构建最优编码方式,可以有效地减少数据的存储空间和传输带宽。
哈夫曼编码模板
#include <iostream>
#include <queue>
using namespace std;
// 哈夫曼树节点
struct HuffmanNode {
char data;
int frequency;
HuffmanNode* left;
HuffmanNode* right;
HuffmanNode(char ch, int freq) {
data = ch;
frequency = freq;
left = right = nullptr;
}
};
// 比较函数,用于优先队列中的排序
struct Compare {
bool operator()(HuffmanNode* a, HuffmanNode* b) {
return a->frequency > b->frequency;
}
};
// 建立哈夫曼树
HuffmanNode* buildHuffmanTree(priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare>& pq) {
while (pq.size() > 1) {
HuffmanNode* left = pq.top();
pq.pop();
HuffmanNode* right = pq.top();
pq.pop();
int sum_freq = left->frequency + right->frequency;
HuffmanNode* parent = new HuffmanNode('$', sum_freq);
parent->left = left;
parent->right = right;
pq.push(parent);
}
return pq.top();
}
// 打印哈夫曼编码
void printHuffmanCodes(HuffmanNode* root, string code) {
if (root == nullptr)
return;
if (root->data != '$')
cout << root->data << ": " << code << endl;
printHuffmanCodes(root->left, code + '0');
printHuffmanCodes(root->right, code + '1');
}
// 主函数
int main() {
string input = "Hello Huffman Tree!";
int frequency[256] = { 0 };
// 统计字符频率
for (char ch : input)
frequency[ch]++;
// 构建优先队列
priority_queue<HuffmanNode*, vector<HuffmanNode*>, Compare> pq;
for (int i = 0; i < 256; i++) {
if (frequency[i] > 0) {
HuffmanNode* node = new HuffmanNode(i, frequency[i]);
pq.push(node);
}
}
// 建立哈夫曼树
HuffmanNode* root = buildHuffmanTree(pq);
// 打印哈夫曼编码
printHuffmanCodes(root, "");
return 0;
}
哈夫曼练习题
7-29 修理牧场
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li 个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
代码:
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
int main()
{
int len, temp, min1, min2, sum = 0;
//greater<int>升序排列,小的在队列出口
priority_queue<int,vector<int>, greater<int> > q;
cin>> len;
for (int i = 0; i < len; ++i) {
cin >> temp;
q.push(temp);
}
while (q.size()>1 ){
min1 = q.top();
q.pop();
min2 = q.top();
q.pop();
//sum代表根节点到每个叶子节点的最小和
sum = sum + min1 + min2;
q.push(min1 + min2);
}
cout << sum;
return 0;
}