1. 程式人生 > 實用技巧 >Huffman編碼

Huffman編碼

Huffman編碼

哈夫曼編碼(Huffman Coding),是一種編碼方式,哈夫曼編碼是可變字長編碼(VLC)的一種。Huffman於1952年提出一種編碼方法,該方法完全依據字元出現概率來構造異字頭的平均長度最短的碼字,有時稱之為最佳編碼,一般就叫做Huffman編碼 。

  1. 預備知識

    • 哈夫曼樹的儲存結構

      typedef struct

      {

      unsigned int weight;

      unsigned int parent, lchild, rchild;

      }HTNode,*HuffmanTree; //動態分配陣列儲存哈夫曼樹

    • 哈夫曼編碼的儲存結構

      typedef char * *HuffmanCode;

      ​ //動態分配陣列儲存哈夫曼編碼表

  2. 實驗題目

    從鍵盤接收任意一個字串。以字串中某字元出現的次數,作為該字元的權值。利用得到的權值構造huffman樹、並輸出每個字元對應的huffman編碼。

程式碼執行的預編譯命令

#include <stdio.h>
#include <malloc.h>
#include <string.h>

所用的函式的宣告以及結構體定義

#include "CommonDef.h"
typedef struct{
	int weight;
	int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct{
	char ch;
	int weight;
	}ww,*W;
typedef char **HuffmanCode;
void select(HuffmanTree *HT,int n,int *s1,int *s2);
void HuffmanCoding(HuffmanTree *HT,W *w,int n);
void Huffman(HuffmanTree HT,HuffmanCode *HC,W *w,int n);

具體的函式實現

Huffman樹的構建

void HuffmanCoding(HuffmanTree *HT,W *w,int n){
	HuffmanTree p;
	int s1,s2,m,i;
	if(n<=1) return;
	m=2*n-1;
	*HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));
	for(p=*HT+1,i=1;i<=n;i++,p++){
		p->weight=w[i-1]->weight;
		p->lchild=0;
		p->parent=0;
		p->rchild=0;
	}
	for(;i<=m;i++,p++){
		p->weight=0;
		p->lchild=0;
		p->parent=0;
		p->rchild=0;
	}
	for(i=n+1;i<=m;i++){
		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;
	}
	for(p=*HT+1,i=1;i<=m;i++){
	printf("%d %d %d %d\n",p->weight,p->parent,p->lchild,p->rchild);
	p++;
	}
}

構建Huffman樹中找查最小值與次小值的實現

void select(HuffmanTree *HT,int n,int *s1,int *s2){
	int i=1,min,cmin;
	HuffmanTree p;
	p=*HT+1;
	while(p->parent!=0){
		p++;
		i++;}
	min=p->weight;*s1=i;//最小 
		p++;i++;
    while(p->parent!=0){
		p++;
		i++;}
	cmin=p->weight;*s2=i;//次小 
	
	for(i=1,p=*HT+1;i<=n;i++,p++){
		if(p->parent==0){
		if(p->weight<min){
			cmin=min;
			min=p->weight;
			*s2=*s1;
			*s1=i;
		}
		else if(p->weight<cmin&&(*s1!=i)){
			cmin=p->weight;
			*s2=i;
		}
	}
	}
}

通過Huffman樹實現Huffman編碼

void Huffman(HuffmanTree HT,HuffmanCode *HC,W *w,int n){
	char *cd;
	int i,start,c,f;
	*HC=(HuffmanCode)malloc((n+1)*sizeof(char *));
	cd=(char *)malloc(n*sizeof(char));
	cd[n-1]='\0';
	for(i=1;i<=n;i++){
		start=n-1;
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent){
			if(HT[f].lchild==c) cd[--start]='0';
			else cd[--start]='1';
		}
		(*HC)[i]=(char *)malloc((n-start)*sizeof(char));
    	strcpy((*HC)[i],&cd[start]);
	}
	free(cd);
}

測試程式碼

int main(){
	W w[26];
	int n,n1,i,j;
	int zifu[26]={0};
	char *s;
	FILE *fp1,*fp2; 
	HuffmanTree HT;
	HuffmanCode HC;
	s=(char *)malloc(100*sizeof(char));
	fp1=fopen("input.txt","r");
	fp2=fopen("output.txt","w");
	if(!fp1)
	{
		printf("can't open file\n");
		return -1;
	}
	fscanf (fp1,"%s",s);
	n=strlen(s);
	for(i=0;i<26;i++){
		w[i]=(W)malloc(sizeof(ww));//結構體陣列分配空間 
	}
	for(i=0;i<n;i++){
		zifu[s[i]-'a']++;
	}
	for(i=0,j=0;i<26;i++){
		if(zifu[i]!=0){
			w[j]->ch=i+'a';
			w[j]->weight=zifu[i];//給每個陣列賦值 
			j++;
		}
	}
	n1=j;
	for(i=0;i<n1;i++){
		printf("%c的權值%d\n",w[i]->ch,w[i]->weight);
		fprintf(fp2,"%c的權值%d\n",w[i]->ch,w[i]->weight);
	}	
	HuffmanCoding(&HT,w,n1);
	Huffman(HT,&HC,w,n1);
	for(i=1;i<=n1;i++){
		printf("%c的編碼:%s\n",w[i-1]->ch,(HC)[i]);
		fprintf(fp2,"%c的編碼:%s\n",w[i-1]->ch,(HC)[i]);
	}
	return 0;
}

一些注意事項

  1. 測試程式碼中的資料是從檔案讀入,然後再寫入檔案中。讀入的檔案全為字母,我的測試資料中全用的小寫字母。

  2. 程式碼首先對讀入字母進行統計出現的個數,然後再實現編碼。

  3. 本來只把算出來的編碼寫入檔案中,但是為了更好觀看,也列印在螢幕中了。

  4. 本來程式碼中無法對應每個字母,如圖

    為了更好看出每個字母對應的編碼,採用了結構體來儲存每個字母的權值,這樣既可以儲存權值,也可以把字母儲存進去。

測試資料

  1. input檔案的資料: aaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbcccccccddddddddeeeeeeeeeeeeeefffffffffffffffffffffffggghhhhhhhhhhh

  2. 所得到的Huffman編碼