哈夫曼编码/译码系统(树应用)
1.问题描述
[题目描述]
利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。
现在有两个功能:
1 发送者:将待传送的字符信息转化为哈夫曼编码。
2 接受者:将接受的编码信息进行译码,得到还原成发送前的字符信息。
[输入]
输入一个小写字母组成的字符串S(1<=strlen(S)<=1e3),表示待传送的字符串。
[输出]
输出为两行,第一行将输入的字符串转化为哈夫曼编码的长度。第二行输出哈夫曼编码译码的长度。
[样例输入]
hailhydra
[样例输出]
25
9
2.需求分析
struct frequence//统计频率
{
char a;//存放字符
int n;//该字符出现的次数
};
typedef struct
{
unsigned int weight; // 用来存放各个结点的权值
unsigned int parent, lchild, rchild;//指向双亲、 孩子结点的指针
}HTNode, *HuffmanTree; //动态分配数组存储赫夫曼树
typedef char **HuffmanCode;//动态分配数组存储赫夫曼编码表
int W[maxsize], *w;//存放字符的权值
int c1 = 0;//输入报文的长度
frequence ch[27];//26个英文字母,0号位置不存储字符,用来存储总共的字符个数,多次出现的只记一次
char a[1001];//存放所有的字符
3.算法设计
1. Frequent()//输入的字符计算出现次数
2. Select(HuffmanTree HT, int i, int &s1, int &s2) 从HT数组的前i个元素中选出weight最小且parent为0的元素,分别将其序号保存在s1和s2中
3. HuffmanCoding(HuffmanTree &HT, HuffmanCode&HC, int *w, int n) //w存放n个权值, 构造哈夫曼树p, 并求出哈夫曼编码hc
4.调试分析
哈夫曼树的建立使用了二叉链表,构造哈夫曼树有点困难,但是参考课本上和网上的代码,得以实现,最后求出最短带权路径长度。
5.实验结果
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<iostream>
#include<stdlib.h>
using namespace std;
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
typedef int Status;
typedef int Boolean;
typedef int ElemType;
#define maxsize 100
struct frequence//统计频率
{
char a;//存放字符
int n;//该字符出现的次数
};
typedef struct
{
unsigned int weight; // 用来存放各个结点的权值
unsigned int parent, lchild, rchild;//指向双亲、 孩子结点的指针
}HTNode, *HuffmanTree; //动态分配数组存储赫夫曼树
typedef char **HuffmanCode;//动态分配数组存储赫夫曼编码表
int W[maxsize], *w;//存放字符的权值
int c1 = 0;//输入报文的长度
frequence ch[27];//26个英文字母,0号位置不存储字符,用来存储总共的字符个数,多次出现的只记一次
char a[1001];//存放所有的字符
void Frequent()//输入的字符,计算出现次数,统计频率
{
int i = 0;
for (; i <= 26; i++)//初始化结构体数组
{
ch[i].n = 0;//初始字符出现次数都为0
}
char c[1001];
cin>>c;
int n=strlen(c);
for(int k=0;k<n;k++)
{
a[c1]=c[k];
bool flag;
i = 1;
flag = false;
for (; i <= ch[0].n; i++)
{
if (c[k] == ch[i].a)
{
flag = true;
break;
}
}
if (flag)//已存在
{
ch[i].n++;
}
else//不存在
{
ch[0].n++;
ch[ch[0].n].n++;
ch[ch[0].n].a = c[k];
}
c1++;
}
for (int j = 1; j <= ch[0].n; j++)
{
W[j - 1] = ch[j].n;
}
w = W;
}
/*
从HT数组的前i个元素中选出weight最小且parent为0的元素,分别将其序号保存在s1和s2中
*/
void Select(HuffmanTree HT, int i, int &s1, int &s2)
{
int j, k = 1;
//先将第一个parent为0的元素的weight值赋给min_weight,留作以后比较用。
//注意,这里不能按照一般的做法,先直接将HT[0].weight赋给min_weight,
//因为如果HT[0].weight的值比较小,那么在第一次构造二叉树时就会被选走,
//而后续的每一轮选择最小权值构造二叉树的比较还是先用HT[0].weight的值来进行判断,
//这样又会再次将其选走,从而产生逻辑上的错误。
while (HT[k].parent != 0)
k++;
s1 = k;
//选出weight最小且parent为-1的元素,并将其序号赋给min
for (j = 1; j <= i; ++j)
if (HT[j].parent == 0 && HT[j].weight<HT[s1].weight)
s1 = j;
k = 1;//选出weight最小的元素后,将其parent置1,使得下一次比较时将其排除在外
while ((HT[k].parent != 0 || k == s1))
k++;
s2 = k;
for (j = 1; j <= i; ++j)
if (HT[j].parent == 0 && HT[j].weight<HT[s2].weight&&j != s1)
s2 = j;
}
void HuffmanCoding(HuffmanTree &HT, HuffmanCode&HC, int *w, int n)
{ //w存放n个权值, 构造哈夫曼树p, 并求出哈夫曼编码hc
int m, i, s1, s2, start, c, f,a[1001],sum=0;
HuffmanTree p;
if (n <= 1)
return;
m = 2 * n - 1;//哈夫曼树一共有m个节点
HT = (HuffmanTree)malloc((m + 1)*sizeof(HTNode));//哈夫曼树的所有结点
for (p = HT + 1, i = 1; i <= n; ++i, ++p, ++w)
{/*叶子结点初始化,数组前n个*/
p->weight = *w;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (; i <= m; ++i, ++p)
{/*非叶子结点初始化,数组后n-1个*/
p->weight = 0;
p->parent = 0;
p->lchild = 0;
p->rchild = 0;
}
for (i = n + 1; i <= m; ++i)
{/*创建非叶子结点, 建哈夫曼树*/
/*在ht[1]~ht[i-1]的范围内选择两个parent为0且weight最小的结点,
其序号分别赋值给s1、 s2返回*/
Select(HT, i - 1, s1, s2);
HT[s1].parent = i;
HT[s2].parent = i;
HT[i].lchild = s1;
HT[i].rchild = s2;
HT[i].weight = HT[s1].weight + HT[s2].weight;
}
//从叶子到根逆向求每个字符的赫夫曼编码
HC = (HuffmanCode)malloc((n + 1)*sizeof(char *));/*分配n个编码的头指针*/
char *cd;
cd = (char *)malloc(n*sizeof(char));//分配求编码的工作空间
cd[n - 1] = '\0';/*从右向左逐位存放编码, 首先存放编码结束符*/
for (i = 1; i <= n; ++i)//逐个字符求赫夫曼编码
{
start = n - 1;//初始化编码起始指针
for (c = i, f = HT[i].parent; f != 0; c = f, f = HT[f].parent)//从叶子到根逆向求编码
if (HT[f].lchild == c)//若当前树节点左叶子不为空,cd中加0(左分支标0),向下递归遍历
cd[--start] = '0';
else//若当前树节点右叶子不为空,cd中加1(右分支标1),向下递归遍历
cd[--start] = '1';
HC[i] = (char*)malloc((n - start)*sizeof(char));//为第i个字符编码分配空间
strcpy(HC[i], &cd[start]);//从cd复制编码到HC
a[i]=strlen(HC[i]);
//cout << ch[i].a << " 哈夫曼编码是" << HC[i] <<" "<<a[i] <<" "<<ch[i].n<<endl;
sum+=a[i]*ch[i].n;//带权路径长度
}
cout <<sum<< endl;
cout<<c1<<endl;
free(cd);//释放空间
}
int main()
{
HuffmanTree HT;
HuffmanCode HC;
int in;
Frequent();
HuffmanCoding(HT, HC, w, ch[0].n);
}