上周日做了数据结构实验报告,感觉还行。
一、课题描述
编制一个运用哈夫曼编码的相关知识对任意文本文件进行编码、解码,并保存相关结果的程序。
二、概要设计(主要思想应根据代码执行顺序了解)
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;
}