【数据结构】哈夫曼树的前期准备(二)

注意: 所有的准备工作只是为了将这个题目分解成很多小问题解决,而最终我写的哈夫曼解压缩代码会与准备工作有很大区别。

根据得到的哈夫曼树,生成编码
在这里插入图片描述
在这里插入图片描述
生成编码需要用到递归函数
递归函数首先确定函数的形参和返回值,明确函数的作用。
其次,确定递归之间的关联。
最后确定终止条件。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

写压缩文件

如何根据一个字符串(由8位二进制组成)确定其对应的ASCLL编码?
在内存中,任何一个ASCLL字符都占用一个字节,一个字节8位二进制
比如’a’ 对应的ASCLL码是97,那么’a’在内存中保存的数据就是97对应的二进制01100001(要保证是8位)
还有了解补码、原码的知识,这里不再赘述。

现在,已知一个字符串str = “01100001”,如何求出对应表示的ASCLL码?

#include<iostream>
using namespace std;
int main() {
	string str = "01100001";
	char c = 0;
	for (int i = 0; i < 8; i++) {
		c = 2 * c + (str[i]-'0');
	}
	cout << c;
	return 0;
}

在这里插入图片描述
现在,我需要再次读取data.txt内容,每次读取一个字节,前面的工作已经可以得到文件中每一个字符对应的哈夫曼编码了。但是此时每个字符对应的哈夫曼不是八位的,所以就可能是多个字符的哈夫曼编码构成一个八位的二进制。还有就是当读完最后一个字符时,对应形成的二进制不一定是八位,所以后面要补0凑成八位二进制。

在这里插入图片描述
验证正确性

输出数据的二进制表示形式
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

文件解压缩

现在,我们要对压缩后的文件进行解压。我们可以获得压缩后文件的内容,要根据读出的哈夫曼编码找出对应的字符。现在遇到的难题是一次读文件读的是字符(8个二进制)但是哈夫曼编码不一定是八位的。

还有一个问题就是先前我们用map_code保存了字符-哈夫曼编码的键值对
但是,现在我们需要根据哈夫曼编码获得对应的字符,所以还需要再用一个map_change保存哈夫曼编码-字符的键值对。
在这里插入图片描述
在这里插入图片描述

其实在写的过程中又遇到了问题。因为之前由于最后一个字节是没有凑成八位后填补的0,但是此时如果读0,它是存在哈夫曼编码中的,所以之前在读data.txt文件时,要记录文件有多少个字节。
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

#pragma once
#include<iostream>
using namespace std;
#include<queue>
#include<unordered_map>
#include<fstream>
#include<bitset>
struct huffmanNode
{
	char element;
	int weight;
	huffmanNode* leftChild, * rightChild;
	huffmanNode(char element = NULL, int weight = 0, huffmanNode* leftChild = NULL,
		huffmanNode* rightChild = NULL) {
		this->element = element;
		this->weight = weight;
		this->leftChild = leftChild;
		this->rightChild = rightChild;
	}

};
struct cmp
{
	bool operator()(huffmanNode* x, huffmanNode* y) {
		return x->weight > y->weight;
	}

};
class huffmanTree
{
private:
	unordered_map<char, int> map_weight;
	unordered_map<char, string> map_code;
	unordered_map<string, char>map_change;
	huffmanNode* root;
	int fileSize;//记录data.txt文件的大小
public:
	huffmanTree();
	~huffmanTree();

	void createTree();
	void compressedFile(const char* path = "D:\\data.txt");
	void outputFile(const char* path = "D:\\newData.txt");
	void dispose(huffmanNode* t);
	void print();
	void print(huffmanNode* t);
	void output();
	void print_binary();
	void getCode();//获取编码
	void Code(huffmanNode* t, string code);

	void uncompress(const char* path = "D:\\test.txt");
};

huffmanTree::huffmanTree()
{
	root = NULL;
}

huffmanTree::~huffmanTree()
{
	dispose(root);
	root = NULL;
}

void huffmanTree::createTree()
{
	if (map_weight.empty()) {
		cout << "未读取文件,请先读取文件!\n";
		return;
	}
	priority_queue<huffmanNode*, vector<huffmanNode*>, cmp> q;
	unordered_map<char, int>::iterator it = map_weight.begin();
	for (; it != map_weight.end(); it++) {
		huffmanNode* node = new huffmanNode(it->first, it->second);

		q.push(node);
	}

	huffmanNode* w, * x, * y;
	while (q.size() > 1) {
		x = q.top();
		q.pop();
		y = q.top();
		q.pop();
		int weight = x->weight + y->weight;
		w = new huffmanNode('?', weight, x, y);
		q.push(w);
	}

	root = q.top();


}

void huffmanTree::compressedFile(const char* path)
{
	ifstream ifs;
	ifs.open(path, ios::in);
	char c;
	fileSize = 0;
	while ((c = ifs.get()) != EOF) {
		map_weight[c]++;
		fileSize++;
	}
	ifs.close();


}

inline void huffmanTree::outputFile(const char* 
	path)
{
	char value = 0;//将要向文件写入的字符
	int wSize = 0;
	ifstream ifs;
	ifs.open("D:\\data.txt",ios::in);
	char c;//保存从data.txt文件中每次读取的字符
	string str;//记录字符c对应的哈夫曼编码

	fstream ofs;
	ofs.open(path, ios::out);
	while ((c = ifs.get()) != EOF) {
		str = map_code[c];
		for (int i = 0; i < str.length(); i++) {
			value = value * 2 + (str[i] - '0');
			wSize++;
			if (wSize == 8) {
				ofs << value;
				wSize = 0;
				value = 0;
			}

			
		}
	}
	if (wSize < 8) {
		value <<= (8 - wSize);
		ofs << value;
	}
	ifs.close();
	ofs.close();


}

void huffmanTree::dispose(huffmanNode* t)
{
	if (t != NULL) {
		dispose(t->leftChild);
		dispose(t->rightChild);
		delete t;
	}
}

void huffmanTree::print()
{
	print(root);
}

void huffmanTree::print(huffmanNode* t)
{
	if (t != NULL) {
		print(t->leftChild);
		cout << t->element;
		print(t->rightChild);

	}
}

void huffmanTree::output()
{
	unordered_map<char, string>::iterator it = map_code.begin();
	for (; it != map_code.end(); it++) {
		cout << it->first << " " << it->second << "\n";
	}
}

inline void huffmanTree::print_binary()
{
	ifstream ifs;
	ifs.open("D:\\newData.txt", ios::in);
	char c;
	while ((c = ifs.get()) != EOF) {
		cout << bitset<8>(c);
	}
	cout << "\n";
	ifs.close();
}

inline void huffmanTree::getCode()
{
	Code(root,"");
}

inline void huffmanTree::Code(huffmanNode* t, string code)
{
	if (t != NULL) {
		if (t->leftChild == NULL && t->rightChild == NULL) {
			map_code[t->element] = code;
			map_change[code] = t->element;
		}
		else {
			Code(t->leftChild, code + "0");
			Code(t->rightChild, code + "1");
		}
	}
}

inline void huffmanTree::uncompress(const char* path)
{
	ifstream ifs;
	ifs.open("D:\\newData.txt", ios::in);
	ofstream ofs;
	ofs.open(path, ios::out);
	char c;
	string coding="";
	int count = 0;
	while ((c = ifs.get()) != EOF) {
		string str = bitset<8>(c).to_string();
		for (int i = 0; i < str.length(); i++) {
			coding += str[i];
			if (map_change.count(coding)) {//代表存在这个编码
				ofs << map_change[coding];
				count++;
				if (count == fileSize)
					break;
				coding = "";

			}
		}
		if (count == fileSize)
			break;
	}

	ofs.close();
	ifs.close();
}


#include<iostream>
using namespace std;
#include"huffmanTree.h"
int main() {
	huffmanTree h;
	h.compressedFile();
	h.createTree();
	h.getCode();
	h.outputFile();
	h.print_binary();
	h.uncompress();

	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
写的内容很粗糙,所以要有后期的修改,一步一步的是实现每一个模块的功能,基本上都是自己做出来的,所以收获感很强。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是一种特殊的二叉结构,用于编码和解码数据。在哈中,每个叶子节点都代表一个字符或符号,并且具有一个与之关联的权值,代表该字符或符号出现的频率或概率。根据哈的概念,我们可以通过给定的叶子节点的权值来构建哈。 对于给定的叶子节点的权值,构建哈的步骤如下: 1. 首先,根据叶子节点的权值从小到大进行排序。 2. 选取权值最小的两个叶子节点,并将它们作为两个子节点创建一个新的父节点。新父节点的权值等于这两个子节点的权值之和。 3. 将这个新的父节点插入到叶子节点中,同时删除原来的两个子节点。 4. 重复步骤2和步骤3,直到只剩下一个节点,即根节点,这个节点就是哈的根节点。 根据题目提供的例子,我们可以看到一种不是建的方法,只使用数组来模拟哈的构造过程。这种方法是通过数组来存储节点的信息,并通过一些特定的计算方式来模拟构建哈的过程。 根据题目的描述,我们需要根据叶子节点的个数和权值来生成哈,并计算所有节点的值与权值的乘积之和。这个问题可以通过构建哈的步骤来解决。首先,我们需要将叶子节点根据权值进行排序。然后,按照步骤2和步骤3构建哈,直到只剩下一个节点。最后,计算所有节点的值与权值的乘积之和。 综上所述,数据结构的例题是通过给定叶子节点的权值来构建哈,并计算所有节点的值与权值的乘积之和。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [超好理解的哈(最优二叉)与例题](https://blog.csdn.net/weixin_45720782/article/details/109316157)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值