算法导论实验六_哈夫曼编码

一、 伪代码

HUFFMAN(C)

N=|C|;

 Q=C;

for i=1 to n-1 

allocate a new node z

z.left=x=EXTRACT-MIN(Q);

z.right=y= EXTRACT-MIN(Q);

z.freq=x.freq+y.freq;

INSERT(Q,z);

return EXTRACT-MIN(Q); 

二、算法分析

 哈夫曼编码是一种无损压缩方法,其一般算法如下:  

A、首先统计信源中各符号出现的概率,按符号出现的概率从大到小排序;

B、把最小的两个概率相加合并成新的概率,与剩余的概率组成新的概率集合;

C、对新的概率集合重新排序,再次把其中最小的两个概率相加,组成新的概率集合。如此重复进行,直到最后两个概率的和为l;

D、分配码字:码字分配从最后一步开始反向进行,对于每次相加的两个概率,给大的赋¨O",小的赋¨1¨(也可以全部相反,如果两个概率相等,  则从中任选一个赋¨O¨,  另一个赋"l¨即可),读出时由该符号开始一直走到最后的概率和¨1¨,将路线上所遇到的¨O¨和¨l¨按最低位到最高位的顺序排好,就是该符号的哈大曼编码。

 例:设一幅灰度级为6(分别用a 1、a2、a3、a4、a5、a6表示)的图像中,各灰度所对应的概率分别为O.40、O.30、O.1 O、O.1 O、O.06、O.O4。现对其进行哈大曼编码,具体步骤如下:

(1)首 先对信源概率从大到小排序,选出最小的两个概率(O.06和O.04),相加得O.1,与其他概率组成新的概率集合(O.4,O.3,O.1,O.1,O.1);

(2)对新的概率集合重新排序,选出最小的两个概率(O.1和O.1),相加得O.2,组成新的概率集合(O.4,O.3,O.1,O.2);

(3)对新的概率集合重新排序(O.4,O.3,O.2,O.1),选出最小的两个概率(O.2和O.1),相加得O.3,组成新的概率集合(O.4,O.3,O.3);

(4)对新的概率集合重新排序,选出最小的两个概率(O.3和O.3),相加得O.6,直到最后两个概率(O.60和O.40)相加和为1;

(5)分配码字。从最后一步反向进行,首先给最后相加的两个概率(O.60和O.40)分配码字,由于O.60大于O.40,于是给O.60赋¨O¨,给O.40赋¨1¨。

(6)最后写出每个符号的哈夫曼编码。以符号a4(对应的概率为O.1)为例,在从O.1到1.O的路径上,它所遇到的赋值(¨O¨或¨1¨)依次为O、O、1、O,将其反向排列成¨O l OO",于是就形成了符号a4的哈大曼码字¨OlOO¨。

三、算法实现

// 哈夫曼编码(算法)

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>

 

typedef char *HuffmanCode;  //动态分配数组,存储哈夫曼编码

 

typedef struct

{

 unsigned int weight;  //用来存放各个结点的权值

 unsigned int parent,LChild,RChild;  //指向双亲、孩子结点的指针

} HTNode, *HuffmanTree;  //动态分配数组,存储哈夫曼树

 

//选择两个parent0,且weight最小的结点s1s2

void Select(HuffmanTree *ht,int n,int *s1,int *s2)

{

 int i,min;

 for(i=1; i<=n; i++)

 {

  if((*ht)[i].parent==0)

  {

   min=i;

   break;

  }

 }

 for(i=1; i<=n; i++)

 {

  if((*ht)[i].parent==0)

  {

   if((*ht)[i].weight<(*ht)[min].weight)

    min=i;

  }

 }

 *s1=min;

 for(i=1; i<=n; i++)

 {

  if((*ht)[i].parent==0 && i!=(*s1))

  {

   min=i;

   break;

  }

 }

 for(i=1; i<=n; i++)

 {

  if((*ht)[i].parent==0 && i!=(*s1))

  {

   if((*ht)[i].weight<(*ht)[min].weight) min=i;

  }

 }

 *s2=min;

}

 

//构造哈夫曼树htw存放已知的n个权值

void CrtHuffmanTree(HuffmanTree *ht,int *w,int n)

{

 int m,i,s1,s2;

 m=2*n-1;

 *ht=(HuffmanTree)malloc((m+1)*sizeof(HTNode));

 for(i=1; i<=n; i++)  //1--n号存放叶子结点,初始化

 {

  (*ht)[i].weight=w[i];

  (*ht)[i].LChild=0;

  (*ht)[i].parent=0;

  (*ht)[i].RChild=0;

 }

 for(i=n+1; i<=m; i++)

 {

  (*ht)[i].weight=0;

  (*ht)[i].LChild=0;

  (*ht)[i].parent=0;

  (*ht)[i].RChild=0;

 } //非叶子结点初始化

 printf("\nHuffmanTree: \n");

 for(i=n+1; i<=m; i++)   //创建非叶子结点,建哈夫曼树

 { //(*ht)[1]~(*ht)[i-1]的范围内选择两个parent0

  //weight最小的结点,其序号分别赋值给s1s2

  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;

  printf("%d (%d, %d)\n",(*ht)[i].weight,(*ht)[s1].weight,(*ht)[s2].weight);

 }

 printf("\n");

} //哈夫曼树建立完毕

 

//从叶子结点到根,逆向求每个叶子结点对应的哈夫曼编码

void CrtHuffmanCode(HuffmanTree *ht, HuffmanCode *hc, int n)

{

 char *cd;

 int i,start,p;

 unsigned int c;

 hc=(HuffmanCode *)malloc((n+1)*sizeof(char *));  //分配n个编码的头指针

 cd=(char *)malloc(n*sizeof(char));  //分配求当前编码的工作空间

 cd[n-1]='\0';  //从右向左逐位存放编码,首先存放编码结束符

 for(i=1; i<=n; i++)  //n个叶子结点对应的哈夫曼编码

 {

  start=n-1;  //初始化编码起始指针

  for(c=i,p=(*ht)[i].parent; p!=0; c=p,p=(*ht)[p].parent)  //从叶子到根结点求编码

   if( (*ht)[p].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);

 for(i=1; i<=n; i++)

  printf("HuffmanCode of %3d is %s\n",(*ht)[i].weight,hc[i]);

 printf("\n");

}

 

int main()

{

 HuffmanTree HT;

 HuffmanCode HC;

 int *w,i,n,wei,m;

 

 printf("\nn = " );

 scanf("%d",&n);

 w=(int *)malloc((n+1)*sizeof(int)); 

 printf("\ninput the %d element's weight:\n",n); 

 srand((unsigned)time(NULL));

 for(i=1; i<=n; i++)

 { 

  printf("%d: ",i); 

  fflush(stdin);

  wei=rand()%100;

  printf("%d",wei);

  printf("\n");

  w[i]=wei;

 }

 CrtHuffmanTree(&HT,w,n);

 CrtHuffmanCode(&HT,&HC,n);

 return 0;

}

 


你可能感兴趣的:(C++,算法,编码,算法导论)