此为数据结构哈夫曼编码实验,因供参考,源地址:https://download.csdn.net/download/weixin_43314490/12094176
IDE为VS2019
#include<iostream>
#include<string>
#include<iomanip>
using namespace std;
struct HNode
{
int weight;
int parent;
int LChild;
int RChild;
};
struct HCode
{
char data;
char code[1000];
};
class Huffman //哈夫曼类
{
private:
HNode* HTree;
HCode* HCodeTable;
int N;
public:
Huffman();//构造函数
void Init(char* s);//初始化函数
void CreateHTree(int a[], int n);//创建赫夫曼树
void CreateTable(char b[], int n);//创建编码表
void Encoding(char* s, char* d);//加密函数
void Decoding(char* s, char* d);//解密函数
void Select(int& x, int& y, int start, int end);//选择权值最小和次小的两个节点
void Reverse(char* s);//反转字符串
void Ratio(char* s, char* d);//计算压缩比
~Huffman();//析构函数
};
Huffman::Huffman()
{
HTree = NULL;
HCodeTable = NULL;
N = 0;
}
void Huffman::Init(char* s)//初始化
{
int a[1000] = { 0 };//储存每个字符的个数
char b[1000] = { 0 };//储存字符的种类
int length = strlen(s);//统计字符串长度
if (length > 1000)//如果字符长度大于10000则不再执行本函数
{
throw length; //异常处理 throw后面的语句将不在执行
}
int j = 0;
for (int i = 0; i < length; i++)//计算每个不同字符的权值
{
char c = s[i];//取字符串中的每个字符
for (j = 0; j < N; j++)//判断字符是否重复
{
if (c == b[j])
{
a[j]++;
break;
}
}
if (j == N)//计算权值
{
a[j] = 1;
b[j] = s[i];
N++;
}
}
CreateHTree(a, N);//创建哈夫曼树
CreateTable(b, N);//创建编码表
}
void Huffman::Select(int& x, int& y, int start, int end)//寻找权重最小的两个数的位置
{
x = 0, y = 0;
while (HTree[x].parent != -1)
{
x++;
}
for (int i = 1; i < end; i++)
{
if (HTree[i].parent != -1)
{
continue;//如果是之前找出的最小值,跳出本次循环
}
if (HTree[i].weight < HTree[x].weight)
{
x = i;
}
}
HTree[x].parent = -2018210071;//防止重复
while (HTree[y].parent != -1)
{
y++;
}
for (int j = 1; j < end; j++)
{
if (HTree[j].parent != -1)
{
continue;
}
if (HTree[j].weight < HTree[y].weight)
{
y = j;
}
}
}
void Huffman::CreateHTree(int a[], int n)
{
HTree = new HNode[2 * n - 1];//申请2n-1个结点内存空间
for (int i = 0; i < n; i++)
{
HTree[i].weight = a[i];//输入权值
HTree[i].LChild = -1;//将哈夫曼树的每个结点数据初始化为1
HTree[i].RChild = -1;
HTree[i].parent = -1;
}
int x, y;//数据中最小的两个数
for (int i = n; i < 2 * n - 1; i++)
{
Select(x, y, 0, i);//获取数据中最小的两个数
HTree[x].parent = HTree[y].parent = i;//生成哈夫曼树的每个结点
HTree[i].weight = HTree[x].weight + HTree[y].weight;
HTree[i].LChild = x;
HTree[i].RChild = y;
HTree[i].parent = -1;
}
}
void Huffman::CreateTable(char b[], int n)//建立编码表
{
HCodeTable = new HCode[n];//申请输出编码表所需的空间
for (int i = 0; i < n; i++)//建立编码表
{
HCodeTable[i].data = b[i];//将每个不同的字符赋值到编码表的数据域
int ChildNum = i;
int parent = HTree[i].parent;
int k = 0;
while (parent != -1)//为每一个不同的字符编码(此循环后获得的编码为所需编码的倒序)
{
if (ChildNum == HTree[parent].LChild)//奇数为左孩子,偶数为右孩子
HCodeTable[i].code[k] = '0';
else
HCodeTable[i].code[k] = '1';
k++;
ChildNum = parent;
parent = HTree[ChildNum].parent;
}
HCodeTable[i].code[k] = '\0';
Reverse(HCodeTable[i].code);//将编码倒序,生成所需的编码
}
for (int i = 0; i < N; i++)//输出编码表
cout << HCodeTable[i].data << "的编码为:" << HCodeTable[i].code << endl;
}
void Huffman::Reverse(char* s)//将编码倒序
{
int length = strlen(s);
char temp[1000];
for (int i = 0; i < length; i++)
temp[i] = s[length - i - 1];
for (int i = 0; i < length; i++)
s[i] = temp[i];
}
void Huffman::Encoding(char* s, char* d)//根据编码表为字符串编码
{
Init(s);//将字符串初始化
int length = strlen(s);
int k = 0;
for (int i = 0; i < length; i++)
{
for (int j = 0; j < N; j++)
{
if (s[i] == HCodeTable[j].data)
{
int p = 0;
while (HCodeTable[j].code[p] != '\0')
{
d[k] = HCodeTable[j].code[p];//按照编码表对字符串中的每个字符进行编码
k++;
p++;
}
}
}
}
d[k] = '\0';//字符串编码结束,方便调用时确定结束条件
}
void Huffman::Decoding(char* s, char* d)//译码
{
while (*s != '\0')
{
int parent = 2 * N - 2;
while (HTree[parent].LChild != -1)
{
if (*s == '0')
parent = HTree[parent].LChild;
else
parent = HTree[parent].RChild;
s++;
}
*d = HCodeTable[parent].data;//把编码后的字符串数组中的每个数据按照编码表反译
d++;
}
*d = '\0';//译码结束,方便调用时确定结束条件
}
void Huffman::Ratio(char* s, char* d)//计算压缩比
{
int n1 = strlen(s);//计算字符串编码前的长度
int n2 = strlen(d);//计算字符串编码后的长度,如果不能整除则向上取整
if (n2 % 8 != 0)
{
n2 = n2 / 8 + 1;
}
else
{
n2 = n2 / 8;
}
cout << "编码前长度为:" << n1 << endl;
cout << "编码后长度为:" << n2 << endl;
cout << "压缩比为:" << double(100 * n2 / n1) << "%" << endl;//输出压缩比
}
Huffman::~Huffman()//析构函数 释放内存
{
delete[]HTree;
delete[]HCodeTable;
}
int main(void)
{
pos:
Huffman H1;
Huffman H2;
char s[1000] = { '\0' }, d[1000] = { '\0' }, q[1000] = { '\0' }, dd[1000] = { '\0' }, qq[1000] = { '\0' };
cout << "********************************************************************************" << endl;
cout << " 欢迎执行哈夫曼编/译码器程序" << endl;
cout << "********************************************************************************" << endl;
char Choice;
cout << " * 菜单 *" << endl;
cout << " ************************" << endl;
cout << " * A. 代码功能测试 *" << endl;
cout << " * B. 进入交互界面 *" << endl;
cout << " ************************" << endl;
cout << "请选择需要进行的操作:";
cin >> Choice;
cin.ignore();
if (Choice == 'A')
{
char text[] = "this is an example of a huffman tree.";
char* t = text;
cout << "测试字符串为:" << t << endl;
cout << "********************************************************************************" << endl;
H1.Encoding(t, dd);
H1.Decoding(dd, qq);
cout << "********************************************************************************" << endl;
cout << "编码结果为:" << dd << endl;
cout << "********************************************************************************" << endl;
cout << "解码结果为:" << qq << endl;
cout << "********************************************************************************" << endl;
H1.Ratio(t, dd);
cout << "********************************************************************************"<<endl;
cout << endl;
}
else
{
try
{
cout << "********************************************************************************" << endl;
cout << "下面进入交互界面:" << endl << endl;
cout << "********************************************************************************"<<endl;
cout << "请输入字符串:" << endl;
cin.getline(s, 1000, '\n');
H2.Encoding(s, d);
H2.Decoding(d, q);
cout << "编码结果为:" << d << endl;
cout << "********************************************************************************" << endl;
cout << "解码结果为:" << q << endl;
cout << "********************************************************************************" << endl;
H2.Ratio(s, d);
cout << "********************************************************************************" << endl;
}
catch (int m)
{
cout << "字符串长度为" << m << ">1000 overflow" << endl;//异常处理,内存泄漏
}
}
char YN;
cout << "是否要退出(Y or N)?" << endl;
cin >> YN;
cin.ignore();
if (YN != 'Y')
{
goto pos;
}
system("pause");
}