#include<stdio.h>
#include<string.h>
#include<malloc.h>
//频率结构体
typedef struct{
char c;
int cnt; //字符出现频率
}WPL;
//哈夫曼树表结构
typedef struct{
char c;
int weight; //权值
int parent,lch,rch; //双亲,左孩子,右孩子
}HuffmanTree;
//统计字符的频率
int setWPLValue(char *s,int size,WPL w[]){
//初始化
for(int i=0;i<size;i++)
w[i].cnt=0;
int flag=1;
int num=0; //统计有多少个不同的字符
while(*s!='\0'){
//如果字符等于w数组中的字符,将这个字符cnt++;并将flag置为0
for(int i=0;i<num;i++){
if(*s==w[i].c){
w[i].cnt++;
flag=0;
break;
}
}
//如果flag为1,将新字符添加到w数组中,将该字符cnt++,并num++
if(flag){
w[num].c=*s;
w[num].cnt++;
num++;
}
flag=1;
s++; //下一个字符
}
return num; //返回数组中有多少个字符
}
//找出最小值和次小值
void Select(HuffmanTree *HT,int size,int *L,int *R){
float min_val; //定义最小值
//从头开始选一个parent不为 0 的数
for(int i=1;i<=size;i++){
if(HT[i].parent==0){
min_val=HT[i].weight;
*L=i;
break;
}
}
//找出parent不为 0 的最小值
for (int i = 1; i <= size; i++){
if (HT[i].weight < min_val && HT[i].parent==0){
min_val = HT[i].weight;
*L = i;
}
}
//从头开始选一个parent不为 0 的数,且被选数序号不能等于被找到的最小值的序号
for(int i=1;i<=size;i++){
if(HT[i].parent==0 && i != *L){
min_val=HT[i].weight;
*R=i;
break;
}
}
//找出parent不为 0 的次小值,且序号不能等于最小值序号
for (int i = 1; i <= size; i++){
if (HT[i].weight < min_val && i != *L && HT[i].parent==0){
min_val = HT[i].weight;
*R = i;
}
}
}
//构造哈夫曼树
HuffmanTree * CreateHuffmanTree(HuffmanTree *HT,WPL w[],int n){
if(n<=1)
return NULL;
int m=2*n-1;
HT=(HuffmanTree *)malloc(sizeof(HuffmanTree)*(m+1)); //因为数组序号从1开始,所以m+1.
//初始化结构体
for(int i=1;i<=m;i++){
HT[i].c=' ';
HT[i].weight=0;
HT[i].lch=0;
HT[i].rch=0;
HT[i].parent=0;
}
//权值赋值
for(int i=1;i<=n;i++){
HT[i].c=w[i-1].c;
HT[i].weight=w[i-1].cnt;
}
//开始建哈夫曼表
for(int i=n+1;i<=m;i++){
int L,R;
Select(HT,i-1,&L,&R); //返回最小值和次小值
//最小值序号为左孩子序号,次小值序号为右孩子序号
if(L>R){
int t=L;
L=R;
R=t;
}
HT[L].parent=i; HT[R].parent=i; //左右孩子的双亲序号
HT[i].lch=L; HT[i].rch=R; //双亲结点的左右孩子序号
HT[i].weight=HT[L].weight + HT[R].weight; //权值为左右孩子权值之和
}
return HT;
}
//编码
char ** enCoding(HuffmanTree *HT,char **HC,int n){ //从叶子到根逆向求每个字符的哈夫曼编码,存储在编码表HC中
HC=(char **)malloc(sizeof(char *)*n+1); //分配n个字符编码的头指针矢量
char *code=(char *)malloc(sizeof(char)*n); //分配临时存放编码的动态数组空间
code[n-1]='\0'; //编码结束符
int start,c,f;
for(int i=1;i<=n;i++){
start=n-1;c=i;f=HT[i].parent;
while(f!=0){ //从叶子结点开始向上回溯,直到根节点
--start; //回溯一次start向前指一个位置
if(HT[f].lch==c)
code[start]='0'; //节点c是f的左孩子,则生成代码0
else
code[start]='1'; //节点c是f的右孩子,则生成代码1
c=f;f=HT[f].parent; //继续向上回溯
} //求出第i个字符的编码
HC[i]=(char *)malloc(sizeof(char)*(n-start)); //为第i个字符串编码分配空间
strcpy(HC[i],code+start);
}
free(code);
code=NULL;
return HC;
}
//解码
void deCoding(char *s,int n,WPL w[],char **hc,HuffmanTree *h){
//压缩码存储
char codeing[5000];
printf("\n\n\t\t\t\t\t\t*** Text after encoding ***\n\n");
while(*s){
for(int i=1;i<=n;i++){
if(*s==w[i-1].c){
strcat(codeing,hc[i]);
printf("%s",hc[i]); //打印编码
break;
}
}
s++;
}
printf("\n\n\t\t\t\t\t\t*** After decoding ***\n\n");
//解码
int id=2*n-1;
int len=strlen(codeing)+1;
int j=0;
while(len){
if(h[id].lch==0){
printf("%c",h[id].c); //打印解码
id=2*n-1;
continue;
}
if(codeing[j]=='0'){
id=h[id].lch;
}else{
id=h[id].rch;
}
j++;
len--;
}
}
//哈夫曼树表
void show(HuffmanTree *H,int n){
printf("\t\t\t\t\t\t*** Huffman Table ***\n\n");
printf(" id\tletter\t\tweight\t\tparent\t\tlch\t\trch\n");
for(int i=1;i<=2*n-1;i++){
if(H[i].c=='\n')
printf(" %d\t\\n\t\t%d\t\t%d\t\t%d\t\t%d\n", i,H[i].weight, H[i].parent, H[i].lch, H[i].rch);
else
printf(" %d\t%c\t\t%d\t\t%d\t\t%d\t\t%d\n", i,H[i].c, H[i].weight, H[i].parent, H[i].lch, H[i].rch);
}
}
int main(void){
char *s="Thoughts of you dance through my mind.\nKnowing, it is just a matter of time.\nWondering... will u ever be mine?\nYou are in my dreams, night... and sometimes... day.\nThe thoughts seem to never fade away.";
printf("\t\t\t\t\t\t*** Text before encoding ***\n\n");
printf("%s\n\n",s);
//统计N个字符的频率
WPL w[strlen(s)];
int n=setWPLValue(s,strlen(s),w);
//构造哈夫曼树
HuffmanTree *h=CreateHuffmanTree(h,w,n);
//打印哈夫曼树表
show(h,n);
//编码
char **hc=enCoding(h,hc,n);
//解码
deCoding(s,n,w,hc,h);
return 0;
}