数据结构之赫夫曼编码
!!!注意!!!
创建文件时请以.cpp结尾,否则报错。
算法思想:
1、创建赫夫曼树:赫夫曼树可以储存在一个大小为2n-1的数组中,数组的每个成员包含了四个信息:权值,双亲,左孩子,右孩子。初始化数组时,可以对前n个元素赋权值,其余成员赋0值;(n+1)到(2n-1)的元素全部赋0。构建赫夫曼树其实就是在森林中寻找双亲为0与权值最小的两个节点,对其赋值,直到仅剩下一个节点时,该节点即为赫夫曼树的根节点。
2、求赫夫曼编码:从叶子到根逆向求编码。从HT【1】开始,判断该节点是左孩子还是右孩子。若为左孩子,则编码’0’;若为右孩子,则编码‘1’。继续寻找该节点的双亲,继续判断,直到双亲=0。此时该字符的赫夫曼编码完成。
流程图:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define TRUE 1 /*状态码预定义*/
#define FALSE 0
#define OK 1
#define ERROR 0
#define INFEASIBLE -1
#define OVERFLOW -2
#define MAXIUMWEIGHT 999999 /*最大权值*/
typedef int Status;
typedef struct
{
unsigned int weight; /*权值*/
unsigned int parent,lchild,rchild; /*双亲,左孩子,右孩子*/
}HTNode,*HuffmanTree;
typedef char * * HuffmanCode; //二级指针
Status Select(HuffmanTree HT,int n,int *s1,int *s2) /*选择函数,找出HT中最小权值的两个节点*/
{
int i,small_1=MAXIUMWEIGHT,small_2=MAXIUMWEIGHT;
*s1=*s2=1;
for(i=1;i<=n;i++)
{
if(HT[i].parent==0) /*无双亲*/
{
if(HT[i].weight<small_1)
{
small_2=small_1; //更新第二最小权值
small_1=HT[i].weight;
*s2=*s1;
*s1=i;
}
else if(HT[i].weight<small_2)
{
small_2=HT[i].weight;
*s2=i;
}
}
}
return OK;
}
Status HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,unsigned int *w,int n) //c++的取引用,所以文件后缀名为.cpp,否则报错。
{
int m,i,s1,s2,start;
int c,f;
char *cd;
HuffmanTree p;
if(n<1) return ERROR;
m=2*n-1; //节点数
HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //给HT分配内存空间,相当于定义了HT[m+1]
p=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); //若用p来对HT操作,则类型与HT需相同,分配相同空间大小,否则运行可能不成功,实际以编译器为准
for(p=HT+1,i=1;i<=n;++i,++p,++w)
{
/*注释里的为MinGW编译器下编译*/
//*(p+1)={w[i-1],0,0,0}; //教材上写*p={w[i-1],0,0,0}; 其实不然,因为HT[0]闲置不用,所以要从p+1开始。p=HT[0]
p->weight=*w; //适用于vc++6.0的写法
p->lchild=0;
p->rchild=0;
p->parent=0;
}
for(;i<=m;++i)
{
/*注释里的为MinGW编译器下编译*/
//HT[i]={0,0,0,0}; //节点置零操作
HT[i].weight=0; //适用于vc++6.0的写法
HT[i].parent=0;
HT[i].rchild=0;
HT[i].lchild=0;
}
for(i=n+1;i<=m;++i) //i从n+1开始是因为前n个节点已有初始权值,且都为叶子
{
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 *)); //相当于HC[n+1] , HC[i]的类型为字符指针类型
cd=(char *)malloc(n*sizeof(char)); //串
cd[n-1]='\0'; //最后一个是终止符
for(i=1;i<=n;++i) //有n个叶子
{
start=n-1; //从叶子开始,所以赋值从后往前赋
for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent) //先找节点,再找双亲f,再找f的双亲,直到根节点
{
if(HT[f].lchild==c) cd[--start]='0'; //如果叶子为左子,则编码0
else cd[--start]='1'; //右子为1
HC[i]=(char*)malloc((n-start)*sizeof(char)); //不确定编码的长度,所以继续分配空间,注意下标为i,表示每个字符的编码
strcpy(HC[i],&cd[start]); //字符串复制
}
}
free(cd); //释放工作空间
return OK;
}
Status PrintHuffmanCode(HuffmanCode HC,unsigned int *w,int n) //简单的打印编码函数
{
printf("Huffman code : \n");
for(int i = 1; i <= n; i++)
printf("%d code = %s\n",w[i-1], HC[i]);
return OK;
}
int main()
{
unsigned int w[8]={2,15,30,8,10,5,12,18}; //字符权值数组
HuffmanCode HC;
HuffmanTree HT;
HuffmanCoding(HT,HC,w,8);
PrintHuffmanCode(HC,w,8);
return 0;
}