1.问题描述
准备一个文件,对该文件各种字符进行Huffman编码,将该文件翻译成Huffman文件。
2.基本要求
(1)设计哈夫曼树的结点存储结构
(2)设计编码和破译方法
(3)输入:源文件
(4)输出:Huffman编码文件
3.程序实现:
(1)代码:
#include<iostream>
#include <fstream>
#include<map>
#include<queue>
#include <string>
using namespace std;
// 二叉树结点
struct HuffmanNode {
HuffmanNode* lChild = nullptr; //左孩子,一定要初始化,否则就会出很麻烦的问题。
HuffmanNode* rChild = nullptr; //右孩子
char Data = '#'; //存储的字符
int Weight; //构建结点的时候存储权重,即频率
HuffmanNode(char Data, int Weight) {
this->Data = Data;
this->Weight = Weight;
}
HuffmanNode() {};
};
// 自定义HuffmanNode比较函数
struct HuffmanNodeCompareWeightGreater
{
bool operator() (const HuffmanNode* a, const HuffmanNode* b)
{
return a->Weight > b->Weight; // 小顶堆
}
};
class HuffmanTree {
HuffmanNode* root;
map <char, string> HuffmanMap; //存储了对应字符和编码的map,解压的时候用与对照还原。
map <char, int> OriginMap; //用于构建哈夫曼树之前统计频率所用,存储的是字符和对应的频率
//字符频率统计,并存入最初的(字符-编码)map中
void GiveWeight(char str);
//构建哈夫曼树,
void CreateHuffmanTree();
//文件读取,并统计字符和频率以此来构建OriginMap
void ReadFile();
//遍历哈夫曼树所得到的字符以及编码得到新的映射,以字符串s形式存储处理后的Huffman编码
void DisplayHuffmanTree(HuffmanNode* Temp, string s);
//Pre为先序遍历,用来检验生成的树是否正确
void Pre(HuffmanNode* p);
public:
HuffmanTree() {
root = new HuffmanNode();
}
//文件压缩并写入,对应着源文件和HuffmanMap中的编码一个一个的输出到新的文件
void ZIP();
//文件解压,
void UNZIP();
};
//构建哈夫曼树,
void HuffmanTree::CreateHuffmanTree() {
//遍历原始的map并根据其键值对逐一构建结点
priority_queue<HuffmanNode*, vector<HuffmanNode*>, HuffmanNodeCompareWeightGreater> HuffmanQueue;
map<char, int>::iterator it = OriginMap.begin();
while (it != OriginMap.end()) {
//这个每循环一次就取出一组键和值
char ch = it->first;
int frequency = it->second;
HuffmanNode* temp = new HuffmanNode(ch, frequency); //构建结点,ch即为每个节点存储的字符
HuffmanQueue.push(temp);
it++;
}
if (!HuffmanQueue.empty())
if (HuffmanQueue.size() == 1)
root->lChild = HuffmanQueue.top();
else if (HuffmanQueue.size() > 1)
while (HuffmanQueue.size() != 1) {
HuffmanNode* hfNode_01 = HuffmanQueue.top();
HuffmanQueue.pop();
HuffmanNode* hfNode_02 = HuffmanQueue.top();
HuffmanQueue.pop();
HuffmanNode* SumNode = new HuffmanNode('#', hfNode_01->Weight + hfNode_02->Weight);
//权重小的放在左孩子位置
if (hfNode_01->Weight < hfNode_02->Weight)
{
SumNode->lChild = hfNode_01;
SumNode->rChild = hfNode_02;
}
else {
SumNode->lChild = hfNode_02;
SumNode->rChild = hfNode_01;
}
HuffmanQueue.push(SumNode);
root = SumNode;
}
cout << "哈夫曼处理后的新编码为: " << endl;
//Pre(root); //检验生成的哈夫曼树是否正确
//构建哈夫曼字符编码映射HuffmanMap
string s;
DisplayHuffmanTree(root, s);
}
void HuffmanTree::DisplayHuffmanTree(HuffmanNode* Temp, string s) {
if (Temp != nullptr) {
if (Temp->Data != '#') {
HuffmanMap.insert(pair<char, string>(Temp->Data, s));
cout << Temp->Data << " " << s << " " << endl;
}
DisplayHuffmanTree(Temp->lChild, s.append("0"));
s.pop_back(); //需要删除上一个递归语句在结尾加上的数字,否则他会影响下面的递归语句
DisplayHuffmanTree(Temp->rChild, s.append("1"));
}
}
void HuffmanTree::GiveWeight(char str) {
//如果OriginMap不含该字符,就将该字符插入map并频率设置为1
if (OriginMap.count(str) == 0) {
OriginMap.insert(pair<char, int>(str, 1));
}
//如果OriginMap含有该字符,就将该字符的频率更新+1
else {
int frequency = OriginMap[str];
++frequency;
OriginMap.erase(str);
OriginMap.insert(pair<char, int>(str, frequency));
}
}
//读文件并统计频率
void HuffmanTree::ReadFile() {
ifstream ifs("C:\\One\\DELL\\Desktop\\压缩文件\\待压缩的文件.txt");
char ch;
cin.unsetf(ios::skipws);
if (!ifs.is_open())
cout << "文件打开失败" << endl;
while (ifs.get(ch))
GiveWeight(ch); // 统计频率
ifs.close();
}
void HuffmanTree::Pre(HuffmanNode* p) {
if (p != NULL) {
cout << p->Weight << " ";
Pre(p->lChild);
Pre(p->rChild);
}
}
void HuffmanTree::ZIP() {
ReadFile();
CreateHuffmanTree();
//对照源文件,把新的编码再存入文件(待完成)
ifstream ifs("C:\\One\\DELL\\Desktop\\压缩文件\\待压缩的文件.txt");
ofstream ofs("C:\\One\\DELL\\Desktop\\压缩文件\\压缩后的文件.txt");
if (!ifs.is_open() || !ofs.is_open()) {
cout << "文件打开失败";
return;
}
char ch;
cin.unsetf(ios::skipws);
while (ifs.get(ch))
ofs << HuffmanMap.find(ch)->second << endl;
ifs.close();
ofs.close();
ofstream ofsHuffmanCode("C:\\One\\DELL\\Desktop\\压缩文件\\字符和生成的哈夫曼编码.txt");
map<char, string > ::iterator it = HuffmanMap.begin();
while (it != HuffmanMap.end()) {
ofsHuffmanCode << it->first << " " << it->second << endl;
it++;
}
}
void HuffmanTree::UNZIP() {
ifstream in("C:\\One\\DELL\\Desktop\\压缩文件\\压缩后的文件.txt");
string line;
ofstream out("C:\\One\\DELL\\Desktop\\压缩文件\\解压后的文件.txt");
if (in.is_open())
{
while (getline(in, line))
{
//通过value找 key
for (map<char, string>::iterator it = HuffmanMap.begin(); it != HuffmanMap.end(); it++)
{
if (it->second == line)
out << it->first;
}
}
}
else // 没有该文件
{
cout << "文件打开失败" << endl;
}
in.close();
out.close();
}
int main() {
HuffmanTree hfTree;
hfTree.ZIP();
hfTree.UNZIP();
}
(2)建立的模型及存储结构:
采用链表结构生成哈夫曼树。
4.测试与运行:
(1) 若未创建文件则输出结果如下:
(2)
自写的txt文件如下:
运行结果: