哈夫曼树(Huffman Tree)是一种特殊的二叉树,通常用于数据压缩的哈夫曼编码。在哈夫曼树中,频率(或权重)较高的节点离根节点较远,而频率较低的节点离根节点较近。这样,我们可以为频率较低的节点分配较短的编码,为频率较高的节点分配较长的编码,从而实现数据的压缩。本文将详细介绍如何使用C++实现哈夫曼树,并附有完整的代码。
一、哈夫曼树的基本概念
- 节点权重:在哈夫曼树中,每个节点都有一个权重,表示该节点在数据中出现的频率。
- 哈夫曼编码:从根节点到每个叶子节点的路径可以用来表示该叶子节点对应的字符的编码。由于哈夫曼树的构造特性,频率较高的字符的编码较长,频率较低的字符的编码较短。
- 最小堆:在构建哈夫曼树的过程中,我们通常使用最小堆来维护节点集合,以便快速找到权重最小的两个节点进行合并。
二、C++实现哈夫曼树的步骤
- 定义节点结构体:包括节点的权重、字符(或索引)、左右子节点指针等。
- 实现最小堆:用于维护节点集合,并实现插入节点、删除最小节点等操作。
- 构建哈夫曼树:从节点集合中取出权重最小的两个节点,合并为一个新节点,并将新节点插入节点集合。重复此过程,直到节点集合中只剩下一个节点,即为哈夫曼树的根节点。
- 生成哈夫曼编码:从根节点开始遍历哈夫曼树,为每个叶子节点生成哈夫曼编码。
三、详细代码实现
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
using namespace std;
struct MinHeapNode {
char data;
unsigned freq;
MinHeapNode* left, *right;
MinHeapNode(char data, unsigned freq) {
this->data = data;
this->freq = freq;
left = right = nullptr;
}
};
struct MinHeap {
priority_queue<MinHeapNode*, vector<MinHeapNode*>, function<bool(MinHeapNode*, MinHeapNode*)>> minHeap;
// 比较函数,用于priority_queue中的元素比较
auto compare = [](MinHeapNode* l, MinHeapNode* r) {
return (l->freq > r->freq);
};
// 插入节点
void insertNode(MinHeapNode* minHeapNode) {
minHeap.push(minHeapNode);
}
// 提取最小节点
MinHeapNode* extractMin() {
MinHeapNode* temp = minHeap.top();
minHeap.pop();
return temp;
}
// 判断堆是否为空
bool isEmpty() {
return minHeap.empty();
}
};
// 构造哈夫曼树
MinHeapNode* buildHuffmanTree(char data[], int freq[], int size) {
MinHeap minHeap;
// 创建节点并插入最小堆
for (int i = 0; i < size; ++i) {
minHeap.insertNode(new MinHeapNode(data[i], freq[i]));
}
// 当堆中只剩下一个节点时,停止构建
while (!minHeap.isEmpty()) {
MinHeapNode* left = minHeap.extractMin();
MinHeapNode* right;
// 如果堆中只剩下一个节点,则将其视为另一个子节点
if (minHeap.isEmpty()) {
right = left;
} else {
right = minHeap.extractMin();
}
// 合并两个节点,并创建一个新的内部节点
MinHeapNode* top = new MinHeapNode('$', left->freq + right->freq);
top->left = left;
top->right = right;
// 将新节点插入最小堆
minHeap.insertNode(top);
}
// 返回根节点
return minHeap.extractMin();
}
// 打印哈夫曼编码
void printCodes(MinHeapNode* root, string str, map<char, string>& huffmanCodes) {
if (!root) return;
if (root->data != '$') {
// 叶子节点,将当前路径保存到编码映射中
huffmanCodes[root->data] = str;
}
// 递归遍历左子树,添加'0'到路径中
printCodes(root->left, str + "0", huffmanCodes);
// 递归遍历右子树,添加'1'到路径中
printCodes(root->right, str + "1", huffmanCodes);
}
// 主函数
int main() {
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
int freq[] = {5, 9, 12, 13, 16, 45};
int size = sizeof(arr) / sizeof(arr[0]);
// 构建哈夫曼树
MinHeapNode* root = buildHuffmanTree(arr, freq, size);
// 打印哈夫曼编码
map<char, string> huffmanCodes;
printCodes(root, "", huffmanCodes);
// 输出哈夫曼编码
cout << "Huffman Codes: " << endl;
for (auto& code : huffmanCodes) {
cout << code.first << ": " << code.second << endl;
}
// 释放哈夫曼树占用的内存(需要编写相应的函数来递归地释放)
return 0;
}
// 注意:为了完整起见,应该编写一个递归函数来释放哈夫曼树占用的内存。
// 示例输出(可能会因权重不同而有所变化):
// Huffman Codes:
// a: 1111
// b: 1110
// c: 110
// d: 10
// e: 01
// f: 00
为了完整实现C++中的哈夫曼树,并包括内存释放的部分,可以添加一个函数来遍历树并释放其内存。下面是完整的代码,包括内存释放的功能:
#include <iostream>
#include <queue>
#include <string>
#include <map>
#include <vector>
using namespace std;
struct MinHeapNode {
char data;
unsigned freq;
MinHeapNode* left, *right;
MinHeapNode(char data, unsigned freq) : data(data), freq(freq), left(nullptr), right(nullptr) {}
~MinHeapNode() {
// 递归释放左右子节点的内存
if (left) delete left;
if (right) delete right;
}
};
struct MinHeap {
priority_queue<MinHeapNode*, vector<MinHeapNode*>, function<bool(MinHeapNode*, MinHeapNode*)>> minHeap;
// 比较函数,用于priority_queue中的元素比较
auto compare = [](MinHeapNode* l, MinHeapNode* r) {
return (l->freq > r->freq);
};
// 插入节点
void insertNode(MinHeapNode* minHeapNode) {
minHeap.push(minHeapNode);
}
// 提取最小节点
MinHeapNode* extractMin() {
MinHeapNode* temp = minHeap.top();
minHeap.pop();
return temp;
}
// 判断堆是否为空
bool isEmpty() {
return minHeap.empty();
}
};
// 构造哈夫曼树
MinHeapNode* buildHuffmanTree(char data[], int freq[], int size) {
MinHeap minHeap;
// 创建节点并插入最小堆
for (int i = 0; i < size; ++i) {
minHeap.insertNode(new MinHeapNode(data[i], freq[i]));
}
// 当堆中只剩下一个节点时,停止构建
while (!minHeap.isEmpty()) {
MinHeapNode* left = minHeap.extractMin();
MinHeapNode* right;
// 如果堆中只剩下一个节点,则将其视为另一个子节点
if (minHeap.isEmpty()) {
right = left;
} else {
right = minHeap.extractMin();
}
// 合并两个节点,并创建一个新的内部节点
MinHeapNode* top = new MinHeapNode('$', left->freq + right->freq);
top->left = left;
top->right = right;
// 将新节点插入最小堆
minHeap.insertNode(top);
}
// 返回根节点
return minHeap.extractMin();
}
// 打印哈夫曼编码
void printCodes(MinHeapNode* root, string str, map<char, string>& huffmanCodes) {
if (!root) return;
if (root->data != '$') {
// 叶子节点,将当前路径保存到编码映射中
huffmanCodes[root->data] = str;
}
// 递归遍历左子树,添加'0'到路径中
printCodes(root->left, str + "0", huffmanCodes);
// 递归遍历右子树,添加'1'到路径中
printCodes(root->right, str + "1", huffmanCodes);
}
// 释放哈夫曼树占用的内存
void freeHuffmanTree(MinHeapNode* root) {
if (!root) return;
// 递归释放左右子树
freeHuffmanTree(root->left);
freeHuffmanTree(root->right);
// 释放当前节点
delete root;
}
// 主函数
int main() {
char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
int freq[] = {5, 9, 12, 13, 16, 45};
int size = sizeof(arr) / sizeof(arr[0]);
// 构建哈夫曼树
MinHeapNode* root = buildHuffmanTree(arr, freq, size);
// 打印哈夫曼编码
map<char, string> huffmanCodes;
printCodes(root, "", huffmanCodes);
// 输出哈夫曼编码
cout << "Huffman Codes: " << endl;
for (auto& code : huffmanCodes) {
cout << code.first << ": " << code.second << endl;
}
// 释放哈夫曼树占用的内存
freeHuffmanTree(root);
return 0;
}
// 示例输出(可能会因权重不同而有所变化):
// Huffman Codes:
// a: 1111
// b: 1110
// c: 110
// d: 10
// e: 01
// f: 00
// 注意:
// 在这个示例中,我们定义了一个简单的MinHeapNode结构体,它包含一个析构函数来递归地释放左右子节点的内存。
// 在freeHuffmanTree函数中,我们递归地调用该函数来释放整个树的内存。
// 在main函数中,我们调用了这个释放函数来确保程序结束时不再占用内存。
//
// 请注意,由于哈夫曼树的构建和内存管理可能因不同的实现而有所不同,因此上述代码仅作为示例。
// 在实际应用中,您可能需要根据自己的需求和环境进行调整和优化。
标签:right,哈夫曼,实现,C++,minHeap,MinHeapNode,freq,节点
From: https://blog.csdn.net/qq_41256535/article/details/139251153