问题描述:输入一串字符串,根据给定的字符串中字符出现的频率建立相应的哈夫曼树,构造哈夫曼编码表,在此基础上对字符串进行压缩(即编码),同时对压缩后的二进制编码串进行解压(即译码)。为简化设计,输入字符串均为 小写英文字母,要求输出:
①统计的字符出现频率(输出格式为 “字符:频度”)
②哈夫曼树构造过程,用哈夫曼树的存储结构的终态表示
③字符串的编码 ④解码的字符串
#include<bits/stdc++.h>
#define UINT_iMAX 100
using namespace std;
typedef struct {
int weight;
char c;
int parent, lchild, rchild;
}HTNode, * HuffmanTree;
typedef char** HuffmanCode;
int Min(HuffmanTree& HT, int i)//用来求出森林中最小的两个节点
{
//在HT[1...i]中选择parent为0且权值最小的结点
//返回该结点的下标值
//此函数被Select函数调用
int j;
unsigned int k = UINT_iMAX;//假设各结点的权值不会超过UINT_MAX
int flag;
for (j = 1; j <= i; ++j)
{
if (HT[j].weight < k && HT[j].parent == 0)//用父结点是否为0来判断此结点是否已经被选过
{
k = HT[j].weight;
flag = j;
}
}
HT[flag].parent = 1;//作个标记,说明已经被选择了,因为在Select函数中要选择权值小的两个结点
return flag;
}
void Select(HuffmanTree& HT, int i, int& s1, int& s2)
{
//在HT[1...i]中选择parent为0且权值最小的两个结点,其序号分别为s1,s2
//s1 <= s2
s1 = Min(HT, i);
s2 = Min(HT, i);
}
void CreateHuffmanTree(HuffmanTree& HT, map<char, int > s,int n)
{
int m;
int i, s1, s2;
if (n <= 1)//如果只有一个就不用创建
return;
m = 2 * n - 1; //总共需要2n-1个节点
HT = new HTNode[m + 1];//开辟空间
for (i = 1; i <= m; i++)//给所有的节点附初始值
{
HT[i].parent = 0;
HT[i].lchild = 0;
HT[i].rchild = 0;
HT[i].c='X';
}
i=1;
for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter) //用迭代器遍历
{
HT[i].c=iter->first;
HT[i].weight=iter->second;
i++;
}
for (i = n + 1; i <= m; i++)
{
Select(HT, i - 1, s1, s2);//在n个数中找出权值最小的两个
HT[s1].parent = i;
HT[s2].parent = i;//将他们两个的parent节点设置为i;
HT[i].lchild = s1;
HT[i].rchild = s2;
//把这两个分别当作左右节点
HT[i].weight = HT[s1].weight + HT[s2].weight;//他们两个的双亲为他们两个的和;
}
}
void CreatHuffmanCode(HuffmanTree HT, HuffmanCode& HC, int n)//赫夫曼编码函数
{
int start, c, f;
int i;
char* cd = new char[n]; //分配临时存放编码的动态数组空间
HC = new char* [n + 1];//分配n个字符编码的头指针矢量
cd[n - 1] = '\0';
for (i = 1; i <= n; i++)//倒序存放字符赫夫曼编码
{
start = n - 1;// start开始时指向最后,即编码的结束符的位置
c = i; f = HT[i].parent;//f指向结点c的双亲结点
while (f != 0) {//从叶子节点开始向上回溯,直到根节点
--start;
if (HT[f].lchild == c)
{
cd[start] = '0';
}
else
{
cd[start] = '1';
}
c = f;
f = HT[f].parent;
}
HC[i] = new char[n - start];//为第i个字符编码分配空间
strcpy(HC[i], &cd[start]);//将求得的编码从临时空间cd复制到HC的当前空间中
}
delete cd;//释放临时变量
}
void Decoding(HuffmanTree& tree,int n) {
printf("请输入要解码的字符串\n");
char ch[100];//输入的待解码的字符串
scanf("%s", ch);
int i;
int num[100];//用于保存字符串对应的0 1 编码对应的节点
int cur;
for (i = 0; i < strlen(ch); i++) {
if (ch[i] == '0')
num[i] = 0;
else
num[i] = 1;
}
if (tree) {
i = 0;
cur = 2*n-1;
while (tree[cur].lchild && tree[cur].rchild) {
if (num[i] == 0)
cur = tree[cur].lchild;
else
cur = tree[cur].rchild;
i++;
}
printf("%c", tree[cur].c);
}
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
char str[100];
cout << "请输入字符串\n";
scanf("%s",str);
int n=0, i;
map<char, int > s; //s是一个映射
char c;
for(i=0;i<strlen(str);i++)
{
c=str[i];
if (isalpha(c)) //是字母
{ //如果是字母全部变成小写形式需要<cctype>头文件
s[c]++;
}
}
for (map<char, int>::iterator iter = s.begin(); iter != s.end(); ++iter) //用迭代器遍历
{
n++;
cout << iter->first << ":" << iter->second << endl; //输出迭代器的第一个参数char类型,第二个参数int类型
}
CreateHuffmanTree(HT, s, n);
CreatHuffmanCode(HT, HC, n);
cout << "赫夫曼树为";
cout << "++++++++++++++\n";
printf("NO\tc\tweight\tparent\tlchild\trchild\n");
for (i = 1; i <= 2 * n - 1; ++i)
{
printf("%d\t%c\t%d\t%d\t%d\t%d\n", i, HT[i].c,HT[i].weight, HT[i].parent, HT[i].lchild, HT[i].rchild);
}
printf("赫夫曼编码为:\n");
for (i = 1; i <= n; ++i)
printf("%d| |-->%s\n", i, HC[i]);
Decoding(HT,n);
return 0;
}