首先需要三个txt文件
huffman.txt文件存入各个节点的值,data.txt存入要编码的字符串,code.txt存放编码的结果
如果出问题就是huffman.txt文件的最后一行不是空行,仔细看图中的光标位置
code.txt为空
//zstu-wcj
#include
#include
#include
using namespace std;
#define max 1000
typedef struct huffmantree//哈夫曼树结点
{
char data;//结点的值
int weight;//结点权值
int parent, lchild, rchild;//父节点,左右结点在数组中的位置下标
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
string chhh;//记录data.txt中的字符串
void Select(HuffmanTree& HT, int m, int& s1, int& s2)//在HT[k]中选择两个其双亲域为0且权值最小的结点,(1<=k<=i-1)
{ // 并返回它们在HT中的序号s1和s2
int i, min1 = 10000, min2 = 10000; //先赋予最大值
for (i = 1; i <= m; i++)//找到未建树的最小值的下标
{
if (HT[i].weight < min1 && HT[i].parent == 0) //当且仅当权值为最小值,且未构建到树中
{
min1 = HT[i].weight;
s1 = i;//记录下标
}
}
for (i = 1; i <= m; i++)//找到未建树的第二小的下标
{
if (HT[i].weight < min2 && HT[i].parent == 0 && i != s1)
{
min2 = HT[i].weight;
s2 = i;//记录下标
}
}
}
void Read_Data_To_Tree(HuffmanTree& HT, int n)
{
int m;
m = 2 * n - 1;//m = 所有结点的个数
for (int i = 0; i <= m; ++i) //将1~m号单元中的双亲、左孩子,右孩子的下标都初始化为0
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
}
ifstream myfile("huffman.txt");
HT[1].data = ' ';//找不到将空格从文件读入的方法
HT[1].weight = 186;//就先自己传了
int len;
for (len = 2; len <= n; ++len)//将huffman.txt的内容读到HT[]中
{
myfile >> HT[len].data >> HT[len].weight;
}
int s1, s2;
for (int i = n + 1; i <= m; ++i)
{
Select(HT, i - 1, s1, s2);
HT[s1].parent = i;
HT[s2].parent = i;
//得到新结点i,将s1和s2的父结点下标由0改为i
HT[i].lchild = s1;
HT[i].rchild = s2; //s1,s2分别作为i的左右孩子
HT[i].weight = HT[s1].weight + HT[s2].weight; //i 的权值为左右孩子权值之和
}
}
void Creat_HuffmanCode(HuffmanTree& HT, HuffmanCode* HC, int n)
{
int c, f, start;
*HC = new char* [n + 1];//*HC指向n个字符数组的第一个数组
char* cd = new char[n];//暂时存放哈夫曼编码
cd[n - 1] = '\0';
for (int i = 1; i <= n; i++)
{
start = n - 1;//猜测每个编码的长度不会超过所有叶子结点的个数之和
c = i;//先存储当前结点的下标
f = HT[i].parent;//父结点的下标
while (f != 0)//当HT[i]有父结点时,自底向上,寻找父结点,直到根结点为止
{
if (HT[f].lchild == c)//当父结点的左孩子==当前结点的下标时
cd[--start] = '0';//左0
else //因为是自底向上,所以是从cd的后往前填充编码
cd[--start] = '1';//右1
c = f;//c存储父结点的下标
f = HT[f].parent;//f为父结点的父结点的下标
}
(*HC)[i] = new char[n - start];//(*HC)[i]指向第i个字符串
strcpy_s((*HC)[i], n - start, &cd[start]);//将cd数组从start到末尾的部分复制到(*HC)[i]中
//该处为避免STL检查使用了strcpy
//但是第二个参数的设置没有仔细地验证
//也许存在差1问题
}
delete[] cd;
}
void data_to_code(HuffmanTree& HT, HuffmanCode* HC, int n)//将正文编码写入code.txt
{
string ch1, ch2;
bool flag = true;//使ch2第一次不加空格
ifstream infile("data.txt");
while (infile >> ch1)//每次都只能读到空格
{
if (flag == false)
{
ch2 += " ";
}
ch2 += ch1;
flag = false;
}
chhh = ch2;//data.txt中的完整字符串
infile.close();//将data.txt的内容存入字符串ch中
int num = ch2.size();
//清空文件内容
string dbfile = "code.txt";
ofstream fileout(dbfile.c_str(), ios::out | ios::trunc);
fileout.close();
for (int i = 0; i < num; i++)//遍历ch2字符串
{
for (int j = 1; j <= n; j++)//从 HT[0].data到HT[n].data匹配字符
{
if (ch2[i] == HT[j].data || ch2[i] - 32 == HT[j].data)//当字符和HT[j].data相等 或 字符的ascii码-32转化为大写字母后与HT[j].data相等时
{
fstream f;
f.open("code.txt", ios::out | ios::app);//文件追加输入方式
f << (*HC)[j];//将HT[j].data 对应的哈夫曼编码输入到文件中
f.close();
}
}
}
cout << "是否要打印各个字符对应的编码(1打印,2不打印):" << endl;
int f = 100;
cin >> f;
if (f == 1)
{
for (int i = 1; i <= n; i++)
{
cout << HT[i].data << " " << (*HC)[i] << endl;
}
}
}
void decodingHuffmanCode(HuffmanTree HT, HuffmanCode* HC, bool* up, int n) {
int p = 2 * n - 1;//HT的最后一个节点是根节点,前n个节点是叶子节点
int i = 0;//指示测试串中的第i个字符
int j = 0;//指示结果串中的第j个字符
char result[100];//存储解码以后的字符串
string coding;//存储code.txt中的哈夫曼编码
ifstream infile("code.txt");
infile >> coding;//将code.txt中的二进制字符串输入到coding
infile.close();
int num = coding.size();//二进制编码长度
while (i < num)//依次读取二进制编码
{
if (coding[i] == '0') p = HT[p].lchild;//若读到的编码为1,则p跳转到左孩子结点
if (coding[i] == '1') p = HT[p].rchild;//若读到的编码为0,则p跳转到左孩子结点
if (p <= n) //p<=n则表明p为叶子节点,因为在构造哈夫曼树HT时,HT的m个节点中前n个节点为叶子节点
{
result[j] = HT[p].data;
j++;
p = 2 * n - 1;//p重新指向根节点
}
i++;
}
result[j] = '\0';//结果串的结束符
//因为 HT[p].data中的数据都为大写字母
//所以result数组中全为大写字母
//下面的for循环通过up数组的判定进行转换
for (int c = 0; c <= j; c++)
{
if (up[c] == 1)//非小写字母时直接输出
{
cout << result[c];
}
else//小写字母时将result的ascii码+32转化为小写字母
{
char down = result[c] + 32;
cout << down;
}
}
}
int main()
{
HuffmanCode HC;
HuffmanTree HT;
fstream fin("huffman.txt", ios::in);
char c;
int line = 0;
while (fin.get(c))//统计data.txt文件的行数,行数line+1即为结点数
{
if (c == '\n')//读到换行符时累加
line++;
}
fin.close();
int lenth = 0;
int n = line + 1;//行数
int m = 2 * n - 1;//结点数
HT = new HTNode[m + 1]; //不用HTNode[0],所以分配m+1个单元,HT[m] 表示根结点
cout << "************哈夫曼编译码***********" << endl;
cout << "*********1.读取数据并建树**********" << endl;
cout << "*********2.编码 **********" << endl;
cout << "*********3.解码并打印 **********" << endl;
cout << "*********4.打印各个编码 **********" << endl;
cout << "***********************************" << endl;
int choice = -1;
while (choice != 0)
{
cout << "请输入:";
cin >> choice;
switch (choice)
{
case 1:
Read_Data_To_Tree(HT, n);//将huffman.txt的数据读入HT中
Creat_HuffmanCode(HT, &HC, n);//根据HT中的权值建树
cout << "建立成功" << endl;
break;
case 2:
data_to_code(HT, &HC, n);//将正文编码写入code.txt
cout << "编码成功" << endl;
break;
case 3:
bool up[100];//字符串中每个字符对应的flag,用来判断是否为大写字母
lenth = chhh.size();//chhh是从data.txt中读到的字符串
for (int i = 0; i < lenth; i++)//
{
up[i] = true; //默认每个字符都是大写
if (chhh[i] > 90) up[i] = false;//当字符的ascii码值>90时设为false (Z的ascii码为90)
}
cout << "解码为:" << endl;
decodingHuffmanCode(HT, &HC, up, n);//解码
cout << endl;
break;
}
}
return 0;
}
有不明白的地方可以加群讨论(没错只有我一个人)484266833