哈夫曼编码——数据结构

上周日做了数据结构实验报告,自己独立完成,感觉还不错,发个博客出来乐呵乐呵。

一、课题描述

   编制一个运用哈夫曼编码的相关知识对任意文本文件进行编码、解码,并保存相关结果的程序。

二、概要设计(主要思想应根据代码执行顺序了解)

1)数据逻辑结构:主要是树形结构,也有使用线性结构作为辅助;
存储结构分析:主要是非线性结构(二叉树)。
   结点:struct HuffTree{
      char data;
      int weight;
      HuffTree*left=NULL, *right=NULL;
      };
2)本程序包含8个函数:
(1)将数据树化函数HuffTree* create(const char c, int i)
 c为字符,i为权值,将其存入一棵最小的二叉树中,左右儿子均为空,然后返回这棵二叉树,配合init()函数使用。
(2)初始化函数void init():
 接受输入的字符及其权值,并将每一组字符和权值树化,存入vector<HuffTree>容器Huff中。
(3)比较函数bool cmp(HuffTree*h1,HuffTree*h2)
 比较两个结点,如果前者权值小于后者,返回TRUE,否则返回FALSE,辅助sort函数使用。
(4)合并两棵树函数HuffTree* merge(HuffTree*t1, HuffTree*t2)
 创建一个新结点,权值为t1和t2的权值之和,char数据为’#’,表示非输入的字符,然后返回这个结点(树)。
 
(5)(主要算法实现)编码函数HuffTree* Encode()
 在容器Huff里面,只要不止一棵树,就进行排序,将权值小的排在前面,按规则合并最小的两棵树(删除原两树,将新树插入),当还剩下一棵树的时候,这棵树就是哈夫曼树,然后把这棵树返回,用FinalTree接收。
(6)得到编码函数void HuffVisit(HuffTree*t, string s)
 遍历FinalTree,以左为1,右为0,每碰到data为字母的子树,就把当前的01字符和字母串存入容器HuffDic中(以字母为key)
(7)译码函数void Decode(HuffTree*t, string s)
 依s中01字符串以1左0右的规则遍历FinalTree,得到的每一个字母均存入string decStr中,得到译码字符串decStr。
(8)文件操作函数void file()
 将HuffDic和decStr中存储的结果写入文件中。
(9)展示函数void show()
 将测试结果打印出来
(10)主函数int main()
 调用其他函数,完成功能。

三、代码编写(注:本代码在Visual Studio 2017下编译运行通过)

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cctype>
#include<fstream>
using namespace std;

struct HuffTree			//树结点结构体
{
	char data;				//数据:字符
	int weight;				//权值
	HuffTree*left = NULL, *right = NULL;	//左右子树
};


typedef pair<char, string> Pair;	//为了方便起见,现将前者简化写法
bool space=false;
HuffTree* FinalTree;			//最终的哈夫曼树
vector<Pair> HuffDic;			//存放每个字符的哈夫曼编码
vector<HuffTree*> Huff;			//存放开始的全部哈夫曼树
string DecodeStr;				//对01字符串解码后的字符串

HuffTree* CreateTree(const char c, int i)	//将数据化为小树
{
	HuffTree*t = new HuffTree;
	t->data = c;
	t->weight = i;
	return t;			//返回小树
}

void Initial()
{
	int n, t;
	char c;
	cout << "请输入数据组数:";				//数据组数:欲编码字母个数
	cin >> n;
	while (n--)
	{
		static int k = 1;
		cout << "第" << k++ << "组(字母,权值):";
		cin >> c >> t;
		Huff.push_back(CreateTree(c, t));		//将小树存Huff中去
	}
	cout << "是否输入空格' '的权值?如果是,请输入正整数;如果否,请输入负数:";			//确认是否为空格编码
	cin >> t;
	if (t >= 0)
	{
		Huff.push_back(CreateTree(' ', t)); 
		space = true;
	}
	
}

bool SortCmp(HuffTree*h1, HuffTree*h2)		//sort的比较函数重载
{
	return h1->weight < h2->weight;			//按权值升序
}


HuffTree* TreeMerge(HuffTree*t1, HuffTree*t2)		//和并两棵小树,使两棵小树成为大树的孩子
{
	HuffTree*t = new HuffTree;
	t->data = '#';							//以#号表示无字符
	t->weight = t1->weight + t2->weight;		//大树的权值是两棵小树权值之和
	t->left = t1;
	t->right = t2;
	return t;
}

HuffTree* GetFinalTree()
{
	//for (int i = 0; i < Huff.size(); i++)
	//{
	//cout << Huff[i]->weight << ' ' << Huff[i]->data << endl;
	//}
	while (Huff.size() > 1)				//Huff内不止一棵树的时候
	{
		sort(Huff.begin(), Huff.end(), SortCmp);	//把最小的树排序到前面
		HuffTree*t1, *t2;
		t1 = Huff.front();
		Huff.erase(Huff.begin());
		t2 = Huff.front();
		Huff.erase(Huff.begin());
		Huff.push_back(TreeMerge(t1, t2));		//合并最小的两棵树
	}
	return Huff.front();				//返回最后剩余的一棵树,即FinalTree
}

void Encode(HuffTree*t, string s)
{
	//if (isalpha(t->data))
	if (t->data != '#')
		HuffDic.push_back(Pair(t->data, s));		//如果字符不是#,则和相应的01字符串一起存入HuffDic中
	if (t->left)						//如果有左子树
	{
		string sl = s;
		sl.push_back('0');					//左边为1,放入string
		Encode(t->left, sl);				//递归
	}
	if (t->right)					//如果有右子树
	{
		string sr = s;
		sr.push_back('1');					//右边为0,放入string
		Encode(t->right, sr);				//递归
	}
}

void Decode(HuffTree*t, string s)
{
	if (t->left == NULL || s.empty())			//当没有孩子或01字符串完了的时候
	{
		DecodeStr.push_back(t->data);				//为相应的字母,取出放到DecodeStr中
		if (!s.empty())
			Decode(FinalTree, s);			//如果s还不为空的话,继续下一个字符的解码
	}
	else
	{
		char front = s.front();					//有孩子且还有01字符串时,去掉首字符,继续遍历
		s.erase(s.begin());
		//首字符为0向左,首字符为1向右
		Decode((front == '0' ? t->left : t->right), s);
	}

}

void FileAction()
{
	fstream f("D:\\test.txt", ios::out);			//打开文件,若不存在便创建
	if (!f)
	{
		cout << "FileError!! Can't read and write file 'test.txt'!!";	//打不开文件即退出函数
		return;
	}
	for (int i = 0; i < HuffDic.size(); i++)
	{
		f << HuffDic[i].first << "——" << HuffDic[i].second << endl;		//将HuffDic中保存的字母及其编码写入文件
	}
	f << DecodeStr;						//将DecodeStr写入文件
	f.close();
	cout << "结果已保存至——'test.txt'!" << endl;
}

void Results()
{
	sort(HuffDic.begin(), HuffDic.end());					//将小的字符排在前,进行打印
	cout << "哈夫曼编码:" << endl;
	for (int i = 0; i < HuffDic.size(); i++)
	{
		cout << HuffDic[i].first << "——" << HuffDic[i].second << endl;	//输出格式
	}
	cout << "请输入欲编码的字符串:";
	//cin >> test;
	char c[100] = { 0 };
	getchar();
	cin.getline(c, 100);				//getline以输入带空格的字符串
	string test(c, c + 100);
	int sym = 0;
	for (string::iterator it = test.begin(); it != test.end(); it++)
	{
		sym = 0;
		for (vector<Pair>::iterator i = HuffDic.begin(); i != HuffDic.end()&!sym; i++)
			if (i->first == *it) sym = 1;			//在HuffDic中是否有对应的编码
		if (sym)			//如果有
		{
			if (*it == ' ')
				cout << HuffDic.front().second;		//如果是空格
			else if (*it)
				cout << HuffDic[*it - 65 + int(space)].second;			//输出测试数据中每个字母的哈夫曼编码
		}
		//else
		//{
		//	cout << "字符串中有未编码字符,编码停止,你可以检查并再次运行.";
		//	system("pause");
		//	exit(0);
		//}
	}
	if (sym)
		cout << "字符串中有未编码字符,编码结果可能错误";
	cout << endl << "请输入欲解码的01编码:"<<endl;
	cin >> test;
	Decode(FinalTree, test);				//输入编码,进行解码
	cout << DecodeStr << endl;
}

int main()
{
	Initial();
	FinalTree = GetFinalTree();
	Encode(FinalTree, "");
	Results();
	FileAction();
	return 0;
}




猜你喜欢

转载自blog.csdn.net/zidian666/article/details/80494767
今日推荐