赫夫曼编码实现

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


首先,赫夫曼(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个单元,以上代码中都用了指针的形式。其实数组和指针的关系还不是很清楚。

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

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值