哈夫曼树压缩文件

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();
}

以上代码来自:(若有侵权,联系删除)c++哈夫曼编码实现文件的压缩和解压。c++_OnlyForBetter的博客-CSDN博客_哈夫曼编码实现文件的压缩和解压https://blog.csdn.net/hacker_zrq/article/details/116426110?ops_request_misc=&request_id=&biz_id=102&utm_term=%E5%93%88%E5%A4%AB%E6%9B%BC%E6%A0%91%E5%AE%9E%E7%8E%B0%E6%96%87%E4%BB%B6%E5%8E%8B%E7%BC%A9c++&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-0-116426110.142%5Ev11%5Epc_search_result_control_group,157%5Ev12%5Econtrol&spm=1018.2226.3001.4187

(2)建立的模型及存储结构

采用链表结构生成哈夫曼树。

 4.测试与运行:

(1) 若未创建文件则输出结果如下:

d9c809580df94a73a48e78d27b7dc5b8.png

 (2)

自写的txt文件如下:

ada25cf4654d46c8b01d9d79e567f534.png

运行结果:

b51dd485576849bea62c24e33bd9b75a.png

 

 

 

 

 

综合实验: 1. 问题描述 利用哈夫曼编码进行通信可以大大提高信道利用率,缩短信息传输时间,降低传输成本。这要求在发送端通过一个编码系统对待传输数据预先编码,在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试为这样的信息收发站编写一个哈夫曼码的编/译码系统。 2. 基本要求 一个完整的系统应具有以下功能: (1) I:初始化(Initialization)。从终端读入字符集大小n,以及n个字符和n个权值,建立哈夫曼树,并将它存于文件hfmTree中。 (2) E:编码(Encoding)。利用已建好的哈夫曼树(如不在内存,则从文件hfmTree中读入),对文件ToBeTran中的正文进行编码,然后将结果存入文件CodeFile中。 (3) D:译码(Decoding)。利用已建好的哈夫曼树将文件CodeFile中的代码进行译码,结果存入文件Textfile中。 (4) P:印代码文件(Print)。将文件CodeFile以紧凑格式显示在终端上,每行50个代码。同时将此字符形式的编码文件写入文件CodePrin中。 (5) T:印哈夫曼树(Tree printing)。将已在内存中的哈夫曼树以直观的方式(比如树)显示在终端上,同时将此字符形式的哈夫曼树写入文件TreePrint 中。 3. 测试数据 用下表给出的字符集和频度的实际统计数据建立哈夫曼树,并实现以下报文的编码和译码:“THIS PROGRAME IS MY FAVORITE”。 字符 A B C D E F G H I J K L M 频度 186 64 13 22 32 103 21 15 47 57 1 5 32 20 字符 N O P Q R S T U V W X Y Z 频度 57 63 15 1 48 51 80 23 8 18 1 16 1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SweetCinderella

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值