数据结构(C语言)-哈夫曼(Huffman)树编码译码操作

文章首发于 2020-11-17

知乎文章:数据结构(C语言)-哈夫曼(Huffman)树编码译码操作

作者:落雨湿红尘(也是我o)

导语

本文使用C语言。对某一输入的字符串,如何对其构造哈夫曼(Huffman)树,并由此树的到字符串中每一个字符的哈夫曼编码

本文哈夫曼树和哈夫曼编码采用顺序存储结构实现

哈夫曼树

给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

                                               

                                                                                          哈夫曼树,图片来源百度百科

哈夫曼编码

在数据通信中,需要将传送的文字转换成二进制的字符串,用0,1码的不同排列来表示字符。例如,需传送的报文为“AFTER DATA EAR ARE ART AREA”,这里用到的字符集为“A,E,R,T,F,D”,各字母出现的次数为{8,4,5,3,1,1}。

现要求为这些字母设计编码。要区别6个字母,最简单的二进制编码方式是等长编码,固定采用3位二进制,可分别用000、001、010、011、100、101对“A,E,R,T,F,D”进行编码发送,当对方接收报文时再按照三位一分进行译码。显然编码的长度取决报文中不同字符的个数。若报文中可能出现26个不同字符,则固定编码长度为5

然而,传送报文时总是希望总长度尽可能短。在实际应用中,各个字符的出现频度或使用次数是不相同的,如A、B、C的使用频率远远高于X、Y、Z,自然会想到设计编码时,让使用频率高的用短码,使用频率低的用长码,以优化整个报文编码

为使不等长编码为前缀编码(即要求一个字符的编码不能是另一个字符编码的前缀),可用字符集中的每个字符作为叶子结点生成一棵编码二叉树,为了获得传送报文的最短长度,可将每个字符的出现频率作为字符结点的权值赋予该结点上,显然字使用频率越小权值越小,权值越小叶子就越靠下,于是频率小编码长,频率高编码短,这样就保证了此树的最小带权路径长度效果上就是传送报文的最短长度

因此,求传送报文的最短长度问题转化为求由字符集中的所有字符作为叶子结点,由字符出现频率作为其权值所产生的哈夫曼树的问题。利用哈夫曼树来设计二进制的前缀编码,既满足前缀编码的条件,又保证报文编码总长最短,该前缀编码称为哈夫曼编码

                                          

                                                                             哈夫曼编码

如上图所示,对于一个字符串“AAABBCCCDDDDE” 来说,很容易知道每个字符出现的频次{3,2,3,4,1}。根据频次,每次选出频次最小的两个结点进行组合,频次相加得到父结点。不断重复此过程,直到产生一颗哈夫曼树。

通过该哈夫曼树,我们可以得到每个字符的哈夫曼编码 A=10,B=001,C=01,D=11,E=000。容易证明,每个字符的编码都是前缀编码

C语言实现哈夫曼编码

网上许多大佬实现哈夫曼树的结点都是采用链式存储结构,而实现哈夫曼编码则是采用指针。

那鄙人就使用顺序存储结构来实现哈夫曼树结点,给大家提供一些思路吧

哈夫曼结点,放在一个数组中,即 HNodeType HuffNodes[]

typedef struct{        //Huffman树结点结构体
    float weight;      //结点权值,这里是字符出现的频率,及频次/字符种类数
    char ch_value;     //该节点对应的字符
    int parent;        //父结点位置索引,初始-1
    int lchild;        //左孩子位置索引,初始-1
    int rchild;        //右孩子位置索引,初始-1
} HNodeType;

哈夫曼编码结构,也采用顺序存储结构(数组)

typedef struct{        //Huffman编码结构体
    int bit[MAXBIT];   //该字符的哈夫曼编码
    int start;         //该编码在数组bit中的开始位置
    char ch_value;     //对应字符
} HCodeType;

接受字符串

void str_input(char str[]) {
    printf("请输入任意子字符串:\n");
    //输入可包含空格的字符串,输入字符串存放在str中
    gets(str);
}

统计字符频次 

int TextStatistics(char text[], char ch[], float weight[], int *length) {
    //统计每种字符的出现频次,返回出现的不同字符的个数ch_index
    //出现的字符存放在ch中,对应字符的出现频次存放在weight中, ch_index为ch中字符种类数
    //length为text长度

	int text_index = 0;  //text字符串索引
	int ch_index = 0;	 //计字符数组增加索引,仅用于出现不同字符时,将该字符加入到ch[]中。仅自增 
	
	int weight_index = 0;//频数更新索引。用于指定weight[]要更新频数的位置		 
				
	while(text[text_index]!='\0'){
		//查找ch中,是否存在字符text[text_index],返回查到的第一个字符的位置 
		char* pos = strchr(ch,text[text_index]);
		
		//如果ch中无该字符。或者ch为空。就将text[text_index]加入到ch中 
		if(ch[0]=='\0'  || pos == NULL ){
	
			//加入到统计字符数组中
			ch[ch_index] = text[text_index];
							  		
			//新增一个字符的频数,当所有字符都统计完之后再计算频率 
			weight[ch_index] += 1;
			ch_index++;
			
		}
		
		//如果字符串中有该字符
		else{
			//找到该字符的索引位置,更新其频数 
			weight_index = pos - ch ; 
			weight[weight_index] += 1;

		}
		
		text_index++; 
	}
	ch[ch_index] = '\0';//添加结束符 
	//根据频数计算频率 
	int index=0;
	while(weight[index]!=0){
		weight[index]/=text_index;
		index++;
	}

	*length = text_index;  // 此时text_index即为源字符串text长度
	return ch_index; //最终 ch_index的值即为text字符串中不同字符的个数 
}

找到权值最小的两个结点

// 从 HuffNodes[0..range]中,找到最小的结点索引赋给s1,s2 。已经找到过的结点索引被储存在out[]中 
void select(HNodeType HuffNodes[],int range,int *s1,int *s2){
	//先找第一个最小值 。 
	float min1 = 5;

	for(int index1=0;index1<=range;index1++){
		
		if(HuffNodes[index1].weight < min1 && HuffNodes[index1].parent ==-1){
			//判断该结点是否被选过。如果该结点parent为0,则其为被选 									
				min1 = HuffNodes[index1].weight;
				*s1 = index1 ; 								
		} 	 
	}
	

	//找第2个最小值 	
	float min2 = 5;
	for(int index2=0;index2<=range ;index2++){		
		if(HuffNodes[index2].weight < min2 && HuffNodes[index2].parent ==-1 && index2!=*s1){			
			//判断该结点是否被选过。还要判断其是否被s1选了 													
				min2 = HuffNodes[index2].weight;
				*s2 = index2 ; 								
		} 	 
	}
	
} 

构造哈夫曼树

//构造一棵Huffman树,树结点存放在HuffNodes中
int HuffmanTree(HNodeType HuffNodes[], float weight[], char ch[], int n){
    //构造一棵Huffman树,树结点存放在HuffNodes中
    if(n>MAXLEAF) {
    	printf("超出叶结点最大数量!\n");
    	return -1;
	}
	if(n<=1) return -1;
	
	int m = 2*n-1;//结点总个数
	 
	int node_index = 0;
	//构造各叶节点	
	for(;node_index < n;node_index++){
	/*
		HuffNodes[node_index]->weight
        HuffNodes[node_index]->ch_value
		HuffNodes[node_index]->parent
		HuffNodes[node_index]->lchild
		HuffNodes[node_index]->rchild
	*/	
		HuffNodes[node_index] ={weight[node_index],ch[node_index],-1,-1,-1};		
	} 
	//构造非叶节点
	for(;node_index<m;node_index++)	HuffNodes[node_index] ={0,0,-1,-1,-1};		
	
	//构建Huffmantree
	 
	int s1,s2;//最小值索引
		
	for(int i = n;i < m;i++) {
		select(HuffNodes,i-1,&s1,&s2);
		HuffNodes[s1].parent = i;
		HuffNodes[s2].parent = i;
		HuffNodes[i].lchild = s1; 
		HuffNodes[i].rchild = s2;
		HuffNodes[i].weight = HuffNodes[s1].weight + HuffNodes[s2].weight;
	
	}

	return m-1; // HuffNodes数组中的最后一位,是Huffman树的根结点索引。
}

生成哈夫曼编码

void HuffmanCode(HNodeType HuffNodes[], HCodeType HuffCodes[], char ch[], int n) {
    //生成Huffman编码,Huffman编码存放在HuffCodes中(一个位置存储一个节点的哈夫曼编码,单个结点的哈夫曼编码在bit数组中)
    int start;
	for(int i =0 ;i<n;i++){
		start = n-2;  
        //字符ch[i]对应的Huffman编码(存储在bit数组中)起始位置,从右往左存.每个结点的哈夫曼编码的长度不超过(叶子结点个数n-1),因此bit数组只需要使用(n-1)的长度。
		//而哈夫曼编码是从哈夫曼树的叶子结点开始一直追溯到根结点按照左右赋予0/1值的,所以倒着编码,bit数组起始下标应该是(n-2),每编码一位就减一
		HuffCodes[i].ch_value = ch[i];  // 存储该字符值
		for(int c = i , f=HuffNodes[i].parent ; f!=-1; c =f,f=HuffNodes[f].parent){//从叶子到根逆向求编码
			if(c == HuffNodes[f].lchild) HuffCodes[i].bit[start--]=0;
			else HuffCodes[i].bit[start--]=1;
		}
		HuffCodes[i].start = start+1;
	}
}

遍历哈夫曼树,使用队列递归方式,这里使用的中序遍历。前序遍历,后序遍历,都差不多

int MidOrderTraverse(HNodeType HuffNodes[], float result[], int root, int resultIndex) {
    //Huffman树的中序遍历,遍历结果存放在result中,返回下一个result位置索引   
    //根节点 为root 
	
	if (root!=-1){	
		resultIndex = MidOrderTraverse( HuffNodes,result,HuffNodes[root].lchild,resultIndex);
		result[resultIndex++] = HuffNodes[root].weight;
		resultIndex = MidOrderTraverse( HuffNodes,result,HuffNodes[root].rchild,resultIndex);															
	}
	
	return resultIndex;
}

encoding函数,用来把原始的输入字符串转换为Huffman编码串

int encoding(HCodeType HuffCodes[],char text[],char ch[],int n, int length, int coding_str[]){
	printf("输入的字符串为:%s\n",text);
	printf("其对应Huffman编码为(逗号分割版本):\n");
	int coding_str_idx = 0;  //编码串索引
	for(int text_idx=0; text_idx<length; text_idx++){//从左到右遍历原字符串中各字符
		
		char* pos = strchr(ch,text[text_idx]);  //查找ch中,字符text[text_idx]索引
		// 字符text[text_idx]对应于ch[pos - ch]
		//其对应的Huffman编码,即保存在HuffCodes[pos - ch].bit中
	    for(int j = HuffCodes[pos - ch].start; j < n-1; j++){
	    	printf("%d", HuffCodes[pos - ch].bit[j]);
	    	coding_str[coding_str_idx++] = HuffCodes[pos - ch].bit[j];
	    }
	    printf(",");
	}
	printf("\n");
	return coding_str_idx;  // 编码串长度
}

译码方法,时间复杂度O(n)

//对Huffman编码串coding_str进行译码。res_str为译码结果
int decoding(HNodeType HuffNodes[], int coding_str[],int length, int root, char res_str[]){
	//从左到右遍历编码串,同时查找Huffman树(从根结点开始);
	//编码串出现0(1),则查看Huffman树左(右)孩子结点,验证其是否为叶子结点。若否,则继续此步骤;
	//若是,则可译出该字符,并再从根结点开始查找。
	int tree_idx = root;  //Huffman树节点索引
	int code_idx = 0; //Huffman编码串索引
	int res_idx = 0;  //译码结果索引
	while(code_idx < length){
	 	while(HuffNodes[tree_idx].lchild != -1 and HuffNodes[tree_idx].rchild != -1){ //验证是否为叶子节点
	 		if(coding_str[code_idx] == 0){  //查看左孩子结点
	 			tree_idx = HuffNodes[tree_idx].lchild;
	 		}
	 		else{  //查看右孩子结点
	 			tree_idx = HuffNodes[tree_idx].rchild;
	 		}
	 		code_idx +=1;
	 	}
	 	res_str[res_idx++] = HuffNodes[tree_idx].ch_value;
	 	tree_idx = root;
	}
	return res_idx;
}

主函数

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXVALUE  100000        //输入文本最大字符个数
#define MAXLEAF   256           //最大叶结点个数,即最大不同字符个数
#define MAXBIT    MAXLEAF-1     //编码最大长度
#define MAXNODE   MAXLEAF*2-1   //最大结点个数
int main(){
	
    HNodeType HuffNodes[MAXNODE];   // 定义一个结点结构体数组
    HCodeType HuffCodes[MAXLEAF];   // 定义一个编码结构体数组
    char text[MAXVALUE+1], ch[MAXLEAF];
    float weight[MAXLEAF], result[MAXNODE];
    int i, j, n, resultIndex, length;

    str_input(text);
	
	// 1.统计字符总数n,输入长度length
    n = TextStatistics(text, ch, weight, &length);
	if(n==1){
		printf("只有一种字符,不用编码了。请多输入几种字符\n");
		return 0;
	}
	
    // 2.构造哈夫曼树和哈夫曼编码
    int root = HuffmanTree(HuffNodes, weight, ch, n);
    if (root<0)
    {
    	printf("Huffman树构造失败");
    	return 0;
    }
    HuffmanCode(HuffNodes, HuffCodes, ch, n);

    for (i=0; i<n; i++) { 
        printf("%c的Huffman编码是:", ch[i]);

        for(j=HuffCodes[i].start; j<n-1; j++)
            printf("%d", HuffCodes[i].bit[j]);

        printf("\n");
    }
    
	
    // 3.输出Huffman树的中序遍历结果
    resultIndex = MidOrderTraverse(HuffNodes, result, root, 0);
    printf("\nHuffman树的中序遍历结果是:");
	
    for (i=0; i<resultIndex; i++){
        if (i < resultIndex-1)
            printf("%.4f, ", result[i]);
        else
            printf("%.4f\n\n", result[i]);
	}

	// 4.转换为Huffman编码
	int coding_str[MAXBIT*length];  //Huffman编码串
	int coding_length = encoding(HuffCodes,text,ch, n, length, coding_str);
	printf("其对应Huffman编码为(无分割版本):\n");
	for (int i = 0; i < coding_length; i++){
		printf("%d", coding_str[i]);
	}
	printf("\n");

	// 5.译码过程
	char res_str[length];  //译码结果
	int res_length = decoding(HuffNodes, coding_str, coding_length, root, res_str);
	printf("Huffman译码结果为:");
	for (int i = 0; i < res_length; i++){
		printf("%c", res_str[i]);
	}
	printf("\n");
//	system("pause");
	return 0;
}

输出结果

以上代码,经过本人调试,没有看出问题来。若有问题,欢迎指正

#include #include #include #include using namespace std; # define MaxN 100//初始设定的最大结点数 # define MaxC 1000//最大编码长度 # define ImpossibleWeight 10000//结点不可能达到的权值 # define n 26//字符集的个数 //-----------哈夫曼的结点结构类型定义----------- typedef struct //定义哈夫曼各结点 { int weight;//权值 int parent;//双亲结点下标 int lchild;//左孩子结点下标 int rchild;//右孩子结点下标 }HTNode,*HuffmanTree;//动态分配数组存储哈夫曼 typedef char**HuffmanCode;//动态分配数组存储哈夫曼编码表 //-------全局变量-------- HuffmanTree HT; HuffmanCode HC; int *w;//权值数组 //const int n=26;//字符集的个数 char *info;//字符值数组 int flag=0;//初始化标记 //********************************************************************** //初始化函数 //函数功能: 从终端读入字符集大小n , 以及n个字符和n个权值,建立哈夫曼,并将它存于文件hfmTree中 //函数参数: //向量HT的前n个分量表示叶子结点,最后一个分量表示根结点,各字符的编码长度不等,所以按实际长度动态分配空间 void Select(HuffmanTree t,int i,int &s1,int &s2) { //s1为最小的两个值中序号最小的那个 int j; int k=ImpossibleWeight;//k的初值为不可能达到的最大权值 for(j=1;j<=i;j++) { if(t[j].weight<k&&t[j].parent==0) {k=t[j].weight; s1=j;} } t[s1].parent=1; k=ImpossibleWeight; for(j=1;j<=i;j++) { if(t[j].weight0),构造哈夫曼HT,并求出n个字符的哈弗曼编码HC { int i,m,c,s1,s2,start,f; HuffmanTree p; char* cd; if(num<=1) return; m=2*num-1;//m为结点数,一棵有n个叶子结点的哈夫曼共有2n-1个结点,可以存储在一个大小为2n-1的一维数组中 HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode));//0号单元未用 //--------初始化哈弗曼------- for(p=HT+1,i=1;iweight=*w; p->parent=0; p->lchild=0; p->rchild=0; } for(i=num+1;iweight=0; p->parent=0; p->lchild=0; p->rchild=0; } //--------建哈夫曼------------- for(i=num+1;i<=m;i++) { Select(HT,i-1,s1,s2);//在HT[1...i-1]选择parent为0且weight最小的两个结点,其序号分别为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; } //-------从叶子到根逆向求每个字符的哈弗曼编码-------- HC=(HuffmanCode)malloc((num+1)*sizeof(char *));//指针数组:分配n个字符编码的头指针向量 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';//判断是左孩子还是右孩子(左为0右为1) else cd[--start]='1'; HC[i]=(char*)malloc((num-start)*sizeof(char*));//按所需长度分配空间 int j,h; strcpy(HC[i],&cd[start]); } free(cd); } //****************初始化函数****************** void Initialization() { flag=1;//标记为已初始化 int i; w=(int*)malloc(n*sizeof(int));//为26个字符权值分配空间 info=(char*)malloc(n*sizeof(char));//为26个字符分配空间 ifstream infile("ABC.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i>info[i]; infile>>w[i]; } infile.close(); cout<<"读入字符成功!"<<endl; HuffmanCoding(HT,HC,w,n); //------------打印编码----------- cout<<"依次显示各个字符的值,权值或频度,编码如下"<<endl; cout<<"字符"<<setw(6)<<"权值"<<setw(11)<<"编码"<<endl; for(i=0;i<n;i++) { cout<<setw(3)<<info[i]; cout<<setw(6)<<w[i]<<setw(12)<<HC[i+1]<<endl; } //---------将建好的哈夫曼写入文件------------ cout<<"下面将哈夫曼写入文件"<<endl; ofstream outfile("hfmTree.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i<n;i++,w++) { outfile<<info[i]<<" "; outfile<<w[i]<<" "; outfile<<HC[i+1]<<" "; } outfile.close(); cout<<"已经将字符与对应的权值,编码写入根目录下文件hfmTree.txt"<<endl; } //*****************输入待编码字符函数************************* void Input() { char string[100]; ofstream outfile("ToBeTran.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } cout<<"请输入你想要编码字符串(字符个数应小于100),以#结束"<>string; for(int i=0;string[i]!='\0';i++) { if(string[i]=='\0') break; outfile<<string[i]; } cout<<"获取报文成功"<<endl; outfile.close(); cout<<"------"<<"已经将报文存入根目录下的ToBeTran.txt文件"<<endl; } //******************编码函数**************** void Encoding() { int i,j; char*string; string=(char*)malloc(MaxN*sizeof(char)); cout<<"下面对根目录下的ToBeTran.txt文件中的字符进行编码"<<endl; ifstream infile("ToBeTran.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;i>string[i]; } for(i=0;i<100;i++) if(string[i]!='#') cout<<string[i]; else break; infile.close(); ofstream outfile("CodeFile.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;string[i]!='#';i++) { for(j=0;j<n;j++) { if(string[i]==info[j]) outfile<<HC[j+1]; } } outfile<<'#'; outfile.close(); free(string); cout<<"编码完成------"; cout<<"编码已写入根目录下的文件CodeFile.txt中"<<endl; } //******************译码函数**************** void Decoding() { int j=0,i; char *code; code=(char*)malloc(MaxC*sizeof(char)); char*string; string=(char*)malloc(MaxN*sizeof(char)); cout<<"下面对根目录下的CodeFile.txt文件中的代码进行译码"<<endl; ifstream infile("CodeFile.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for( i=0;i>code[i]; if(code[i]!='#') { cout<<code[i]; } else break; } infile.close(); int m=2*n-1; for(i=0;code[i-1]!='#';i++) { if(HT[m].lchild==0) { string[j]=info[m-1]; j++; m=2*n-1; i--; } else if(code[i]=='1') m=HT[m].rchild; else if(code[i]=='0') m=HT[m].lchild; } string[j]='#'; ofstream outfile("TextFile.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } cout<<"的译码为------"<<endl; for( i=0;string[i]!='#';i++) { outfile<<string[i]; cout<<string[i]; } outfile<<'#'; outfile.close(); cout<<"------译码完成------"<<endl; cout<<"译码结果已写入根目录下的文件TextFile.txt中"<<endl; free(code); free(string); } //*************打印编码函数**************** void Code_printing() { int i; char *code; code=(char*)malloc(MaxC*sizeof(char)); cout<<"下面打印根目录下文件CodeFile.txt中的编码"<<endl; ifstream infile("CodeFile.txt",ios::in); if(!infile) { cerr<<"打开失败"<<endl; exit(1); } for( i=0;i>code[i]; if(code[i]!='#') cout<<code[i]; else break; } infile.close(); cout<<endl; ofstream outfile("CodePrin.txt",ios::out); if(!outfile) { cerr<<"打开失败"<<endl; exit(1); } for(i=0;code[i]!='#';i++) { outfile<<code[i]; } outfile.close(); free(code); cout<<"------打印结束------"<<endl; cout<<"该字符形式的编码文件已写入文件CodePrin.txt中"<<endl; } //*************打印哈夫曼函数**************** int numb=0; void coprint(HuffmanTree start,HuffmanTree HT) //start=ht+26这是一个递归算法 { if(start!=HT) { ofstream outfile("TreePrint.txt",ios::out); if(!outfile) { cerr<<"打开失败"<rchild,HT); //递归先序遍历 cout<<setw(5*numb)<weight<rchild==0) cout<<info[start-HT-1]<<endl; outfile<weight; coprint(HT+start->lchild,HT); numb--; outfile.close(); } } void Tree_printing(HuffmanTree HT,int num) { HuffmanTree p; p=HT+2*num-1; //p=HT+26 cout<<"下面打印赫夫曼"<<endl; coprint(p,HT); //p=HT+26 cout<<"打印工作结束"<<endl; } //*************主函数************************** int main() { char choice; do{ cout<<"************哈弗曼编/译码器系统***************"<<endl; cout<<"请选择您所需功能:"<<endl; cout<<":初始化哈弗曼"<<endl; cout<<":输入待编码字符串"<<endl; cout<<":利用已建好的哈夫曼进行编码"<<endl; cout<<":利用已建好的哈夫曼进行译码"<<endl; cout<<":打印代码文件"<<endl; cout<<":打印哈夫曼"<<endl; cout<<":退出"<<endl; if(flag==0) { cout<<"请先初始化哈夫曼,输入I"<<endl; cout<<""<>choice; switch(choice) { case 'I':Initialization();break; case 'W':Input();break; case 'E':Encoding();break; case 'D':Decoding();break; case 'P':Code_printing();break; case 'T':Tree_printing(HT,n);break; case 'Q':;break; default:cout<<"输入的命令出错,请重新输入!"<<endl; } }while(choice!='Q'); free(w); free(info); free(HT); free(HC); system("pause"); return 0; }
哈夫曼编码是一种基于二叉编码方式,可以实现无损压缩数据的功能。下面是C语言实现哈夫曼编码译码的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> // 哈夫曼的节点结构体 typedef struct huffman_node { char data; // 数据 int freq; // 频率 struct huffman_node *left; // 左子节点 struct huffman_node *right; // 右子节点 } huffman_node_t; // 哈夫曼编码表的结构体 typedef struct huffman_table { char data; // 数据 char code[256]; // 编码 } huffman_table_t; // 构建哈夫曼 huffman_node_t* build_huffman_tree(char* data, int* freq, int n) { huffman_node_t **nodes = (huffman_node_t**)malloc(n * sizeof(huffman_node_t*)); for (int i = 0; i < n; i++) { huffman_node_t *node = (huffman_node_t*)malloc(sizeof(huffman_node_t)); node->data = data[i]; node->freq = freq[i]; node->left = NULL; node->right = NULL; nodes[i] = node; } while (n > 1) { // 找出频率最小的两个节点 int min1 = 0, min2 = 1; if (nodes[0]->freq > nodes[1]->freq) { min1 = 1; min2 = 0; } for (int i = 2; i < n; i++) { if (nodes[i]->freq < nodes[min1]->freq) { min2 = min1; min1 = i; } else if (nodes[i]->freq < nodes[min2]->freq) { min2 = i; } } // 合并频率最小的两个节点 huffman_node_t *parent = (huffman_node_t*)malloc(sizeof(huffman_node_t)); parent->data = 0; parent->freq = nodes[min1]->freq + nodes[min2]->freq; parent->left = nodes[min1]; parent->right = nodes[min2]; if (min1 < min2) { nodes[min1] = parent; nodes[min2] = nodes[n - 1]; } else { nodes[min2] = parent; nodes[min1] = nodes[n - 1]; } n--; } return nodes[0]; } // 生成哈夫曼编码表 void generate_huffman_table(huffman_node_t *root, char* code, int depth, huffman_table_t* table) { if (root->left == NULL && root->right == NULL) { table[root->data].data = root->data; strcpy(table[root->data].code, code); return; } code[depth] = '0'; generate_huffman_table(root->left, code, depth + 1, table); code[depth] = '1'; generate_huffman_table(root->right, code, depth + 1, table); } // 哈夫曼编码 char* huffman_encode(char* data, huffman_table_t* table) { int n = strlen(data); char* code = (char*)malloc(n * 256 * sizeof(char)); int k = 0; for (int i = 0; i < n; i++) { strcat(code, table[data[i]].code); } return code; } // 哈夫曼解码 char* huffman_decode(char* code, huffman_node_t* root) { int n = strlen(code); char* data = (char*)malloc(n * sizeof(char)); huffman_node_t* p = root; int k = 0; for (int i = 0; i < n; i++) { if (code[i] == '0') { p = p->left; } else { p = p->right; } if (p->left == NULL && p->right == NULL) { data[k++] = p->data; p = root; } } return data; } int main() { char data[] = "ABCDAAAABBBCDEFG"; int freq[] = {3, 4, 3, 2, 2, 2, 1, 2, 1, 1}; int n = strlen(data); // 构建哈夫曼 huffman_node_t* root = build_huffman_tree(data, freq, n); // 生成哈夫曼编码表 char code[256]; memset(code, 0, sizeof(code)); huffman_table_t table[256]; generate_huffman_table(root, code, 0, table); // 哈夫曼编码 char* encoded = huffman_encode(data, table); // 哈夫曼解码 char* decoded = huffman_decode(encoded, root); printf("原始数据:%s\n", data); printf("哈夫曼编码:%s\n", encoded); printf("哈夫曼解码:%s\n", decoded); return 0; } ``` 以上代码实现了哈夫曼的构建、哈夫曼编码表的生成、哈夫曼编码哈夫曼解码的功能。在实际应用中,需要根据具体的需求对代码进行修改和优化。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落雨湿红尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值