哈夫曼编码应用在于对于文件的压缩,压缩效率是非常的高。实现哈夫曼编码,得首先知道哈夫曼树的形成过程是怎样进行的:
1、对于所要编码的数据,首先得将它们中找到其中的最小的两个位置合并成一个小树,节点的权值是两者相加形成的;
2、步骤1中得到的权值在进入原排列中,在此获取新组合中的最小的两个数据,在执行步骤1;
3、对于步骤1、2要重复执行它,执行的次数其实可以思考一下,其实有n个数,执行的次数一定是n-1次而已。
以上是哈夫曼编码的大致形成过程,其实有很多细节的东西,还得去思考。
数据格式:
#define N 200
typedef struct huff {
int weight;
char value;
structhuff*Lchild;
structhuff*Rchild;
} HuffumanNode;
1⃣️、这里哈夫曼树的形成过程:
HuffumanNode * CreateTree(int w[],char *str,int n)
{
//n代表数据长度
HuffumanNode**container,*Huffumantree=NULL;
//申请一个空间去保存数据
container=(HuffumanNode**)malloc(n*sizeof(HuffumanNode));
//save data
for(int i=0;i<n;i++){
container[i]=(HuffumanNode*)malloc(sizeof(HuffumanNode));
container[i]->weight=w[i];
container[i]->value=str[i];
container[i]->Lchild=container[i]->Rchild=NULL;
}//这里你要保存数据当然不适用地址,而是用空间去保存,这里要去申请一个空间;
//下面我们是不是要进入创建树的过程了,那么它执行的此时就是n-1次
for(int i=1;i<n;i++)
{
//这里我们得先找出其中最小的权值两个数据组成树
int small1=-1,small2;
for(int j=0;j<n;j++)
{
if(small1==-1&&container[j]!=NULL)
{
small1=j;
continue;
}
if(container[j]!=NULL)
{
small2=j;
break;
}
}
//这里获取两个小标例如0,1
for(int j=small2;j<n;j++)
{
if(container[j]){
if(container[j]->weight<container[small1]->weight){
small2=small1;
small1=j;
}
if(container[j]->weight<container[samll2]->weight)
small2=j;
}
}
//这里就获取最小两个数据的下标了。
Huffumantree=(HuffumanNode*)malloc(sizeof(HuffumanNode));
Huffumantree->weight=container[small1]->weight+container[small2]->weight;
//这里你可以把小的放在左边,当然也可以是右边,都是一样的只要下面换一下就可以实现了.
Huffumantree->Lchild=container[small1];
Huffumantree->Rchild=container[small2];
container[small1]=Huffumantree;
//这点非常重要的,这就是在此进入容器中参与找出最小的两个数据,指针下移
container[small2]=NULL:
}
}
我们可以看一下树建成功了,打印一下。
非递归前序打印一下:
//stack---->
#define InISize 20
#define AppSize 50
typedef struct Stack{
int Capacity;
HuffumanNode*base;
HuffumanNode*top;
}Stack;
typedef struct queue{
HuffumanNode *rear;
HuffumanNode *front;
int size;
}Queue;
void IniStack(Stack *s)
{
s->base=(HuffumanNode*)malloc(InISize*sizeof(HuffumanNode));
if(!s->base)
return ;
s->top=s->base;
s->Capacity=InISize;
}
int EmptyStack(Stack *s)
{
if(s->base==s->top)
{
return 1;
}
else
return 2;
}
void ClearStack(Stack *s)
{
s->top=s->base;
}
void Push(Stack *s,HuffumanNode d)
{
if(s->top-s->base>=s->Capacity)
{
s->base=(HuffumanNode *)realloc(s->base,AppSize*sizeof(HuffumanNode));
if(!s->base)
return ;
s->top=s->base+s->Capacity;
s->Capacity=AppSize+InISize;
}
*(s->top)=d;
s->top++;
}
void pop(Stack *s,HuffumanNode *D)
{
if(s->top==s->base)
return ;
s->top--;
*D=*(s->top);
}
void destroyStack(Stack *s)
{
free(s->base);
s->top=s->base;
s->Capacity=0;
}
int StackSize(Stack *s)
{
return (int)(s->top-s->base);
}
打印一下:
void NoRecuirPrevios(HuffumanNode *tree)
{
HuffumanNode*p=tree;
Stack S;
IniStack(&S);
printf("(");
while(EmptyStack(&S)!=1||p)
{
if(p)
{
printf("%d",p->weight);
printf(",");
Push(&S, *p);
p=p->Lchild;
}
else
{
p=(HuffumanNode*)malloc(sizeof(HuffumanNode));
pop(&S, p);
p=p->Rchild;
}
}
printf(")\n");
}
//接下来表示打印哈夫曼编码了:
左子树打印0,右子树打印1;
我其实考虑用深度递归打印,那样非常简单的:
void HUffmanCoding(HuffumanNode *huffmantree,int deepth)
{
staticint code[N];
if(huffmantree)
{
//这里是从叶子的底层开始递归的,谈判断的条件便是底层便是左右孩子都没有时候就打印,这就是递归的方法;
if(huffmantree->Lchild==NULL && huffmantree->Rchild==NULL)
{
cout<<"该编码对应的值是:"<<huffmantree->value<<",权值是:"<<huffmantree->weight<<"对应的编码是:";
int i;
for( i=0;i<deepth;++i)
{
cout<<code[i];
}
cout<<endl;
}
else
{
code[deepth]=0;
HUffmanCoding(huffmantree->Lchild, deepth+1);
code[deepth]=1;
HUffmanCoding(huffmantree->Rchild, deepth+1);
}
}
}用例测试一下:
这里还有一个更佳简单的就是用数组来实现的:
数据封装:
typedef struct HuffRec
{
double w;
int Pr, Lch, Rch;
}HuffRec, *HuffRecPtr;
int GetMinPos(int m)
{
int k, s;
double Min;
k= 0;
Min= 9999.99;
for(s=0; s<=m; s++)
{
if (HT[s].Pr==0 && HT[s].w < Min)
{
k=s;
Min= HT[s].w;
}
}
return k;}
for(m= N; m< 2*N- 1; m++)
{
i= GetMinPos(m-1);
HT[i].Pr=m;
j= GetMinPos(m-1);
HT[m].Lch= i;
HT[m].Rch= j;
HT[m].w= HT[i].w + HT[j].w;
HT[j].Pr= m;
}
int GenerateCode()
{
int i, j, m, pr;
for(i= 0; i<N; i++)
//
for(j=0; j<N; j++)
{
Code[i][j]= 0;
}
//
for (i= 0; i<N; i++)
Start[i]= 0;
for(i=0; i<N; i++)
{
m= N;
j= i;
//
while(j< 2*N-2)
{
pr= HT[j].Pr;
m--;
if (j== HT[pr].Lch)
Code[i][m]= 0;
else
Code[i][m]= 1;
j= pr;
}
//
Start[i]= m;
}
return 0;
}
for(i= 0; i<N; i++)
{
printf("权值%5.1lf的哈夫曼编码是:", HT[i].w);
//
for(j= Start[i]; j<N; j++)
printf("%d", Code[i][j]);
//
printf("\n");
}
这是其他的方法,不过我认为前面的方法还要好一点,链表要比数组节约空间。(数组是我们老师给大家参考举例而写的)