赫夫曼编码实现

来源:互联网 发布:域名在哪里购买 编辑:程序博客网 时间:2024/06/06 02:34

断断续续的用了两天时间,终于把传说中的赫夫曼树实现了,之前早都听说过,可是都被题目的难度给吓到了,这次有了前面的经验,决心要把赫夫曼编码实现。(当然其实应该也没有太大难度,但是对于我这么一个本科时候数据结构学的那么渣的人来说,能写出来还是有一点成就感的。)


首先,赫夫曼(Huffman)树:带权路径长度最小的二叉树。


赫夫曼算法:

1.根据给定的n个权值构成n棵二叉树的集合F,其中每棵二叉树只有一个带权为wi的根节点,其左右子树均空。

2.选择两棵根节点权值最小的树作为左右子树(这里有没有顺序?较小的靠左?为了统一规范,下面约定,较小的树为左子树)构造一棵新的二叉树,且置新的二叉树的根节点的权值为其左右子树根节点的权值之和。

3.在F中删除这两棵树,同时将新得到的二叉树加入F。

4.重复2和3,直到F只含一棵树为止。这棵树便是赫夫曼树。


下面给出赫夫曼编码的代码:

#include<stdio.h>#include<stdlib.h>#include<string.h>#define MAX 10000;typedef struct HTnode{   unsigned int weight;   unsigned int parent,lchild,rchild;}HTNode,*HuffmanTree;typedef char* *HuffmanCode;HuffmanCode HC;char* cd;int SelectMin(int n,int *p){   int min=100;   int *q;   int m,i;   q=p;     /* for(i=0;i<n;i++)   { printf("%d",i);    // p=p+i;//这样赋值第三个值是错误的,不知为什么         printf("%d",*(p+i));   }*/  for( i=0;i<n;i++)  {     if(*(p+i)<min){  min=*(p+i);}         }  //printf("%d",min);  for( i=0;i<n;i++)  {// q=q+i;//不能这样写,否则最后一个会出问题,但不知为什么    if(*(q+i)==min){    m=i+1;   break;}    }  return m;}void creatHuffmanTree(int n,int *w){  if(n<=1)  return;  int m=2*n-1;//有m个节点,要分配m个节点的空间  int i,c,f;  int s1,s2,weight1,weight2,start;  HuffmanTree HT,p,temp,s;  int *q;  q=w;    HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//第一个单元不用,从HT的下一个单元开始分配,每个节点对应一个  p=HT+1;  s=HT+1;  temp=HT;  for( i=0;i<n;i++)  {     (s+i)->weight=*(w+i);(s+i)->lchild=0;(s+i)->rchild=0;(s+i)->parent=0;       }  for(i=n+1;i<=m;i++)  {   (s+i-1)->weight=0;   (s+i-1)->lchild=0;   (s+i-1)->rchild=0;   (s+i-1)->parent=0;  // p++;//此时p指向下一个未分配的单元    }  for(i=n+1;i<=m;i++){  s1=SelectMin(m,q);    for(int j=0;j<s1;j++) {   temp++;   q++; } temp->parent=i;temp->weight=*(q-1);weight1=temp->weight;*(q-1)=MAX;temp=HT;q=w;s2=SelectMin(m,q);for( j=0;j<s2;j++){  temp++;  q++;}temp->parent=i;temp->weight=*(q-1);weight2=temp->weight;*(q-1)=MAX;temp=HT;q=w;for( j=0;j<i;j++){ temp++; q++;}temp->lchild=s1;temp->rchild=s2;temp->weight=weight1+weight2;*(q-1)=temp->weight;temp=HT;q=w;}//赫夫曼树构造完毕,从叶子节点逆向求每个字符的赫夫曼编码for(int k=1;k<=m;k++){  printf("%d",(HT+k)->weight);   printf("%d",(HT+k)->parent);    printf("%d",(HT+k)->lchild); printf("%d\n ",(HT+k)->rchild);}HC=(HuffmanCode)malloc((n+1)*sizeof(char*));//HC指向头指针cd=(char*)malloc(n*sizeof(char));//分配输入字符的内存cd[n-1]='\0';int j;for(i=1;i<=n;i++){  start=n-1;//每个字符对应编码的结束符位置  c=i;  for( j=0;j<c;j++)  {        temp++;   }   f=temp->parent;   temp=HT; while(f!=0)  {  for( j=0;j<f;j++)  {        temp++;   }      if(temp->lchild==c){ start--; cd[start]='0';}else{ start--; cd[start]='1';}    c=f;f=temp->parent;    temp=HT;  } *(HC+i)=(char*)malloc((n-start)*sizeof(char)); strcpy(*(HC+i),&cd[start]); int k=0; while(*(*(HC+i)+k)!='\0') {   printf("%c",*(*(HC+i)+k));   k++;  } printf("\n");}}int main(){  int n,m,a;  int *p,*q,*temp;    printf("请输入字符个数");  scanf("%d",&n);  m=2*n-1;  p=(int *)malloc(m*sizeof(int));  q=p;  for(int i=0;i<m;i++)  {printf("请输入这个字符对应的权重");    scanf("%d",&a);*(p+i)=a;  } creatHuffmanTree(n,p); return 0;}

通过这次代码的实现,其实有很多学习的地方:

1.strcpy函数的使用

2.分配完编码的工作空间后可以直接使用数组访问,如cd[n-1]

3.双重指针,有点绕

4.编码的规范和效率,细节问题,如题目中有一处赋值出错,导致调试了好半天


到这里,差不多工作也完成了,但是还是感觉有好多问题没有搞清楚,比如指针和数组的关系,比如指针赋值出错。最近的工作重心逐渐从Java,android转移到c过来了,不知道这样好不好,但每次完成一个算法,还是很有成就感的。

对待生活中的人和事要宽容,但是对待代码必须苛刻,认真谨慎,精益求精。现在可能还处在c的初级阶段,关于内存优化的问题还没有接触到,慢慢来吧,不要想太多结果,走好眼前的路就对了。

坚持跑步,坚持coding!

今天是618,各大网购网站又开始促销了,在京东上买了两百多东西,有满减100,一直没搞明白,这些电子网站是靠什么盈利的。


补充:

今天早上来回过头来看了一下,发现赫夫曼实现中有些地方实现复杂了,类似于:

HT=(HuffmanTree)malloc((m+1)*sizeof(HTnode));
都可以直接用数组的形式HT[i]访问其第i+1个单元,以上代码中都用了指针的形式。其实数组和指针的关系还不是很清楚。

数组名就是首地址。一维数组名可以当做指针直接传递

0 0