霍夫曼(Huffman)编码算法详解之C语言版

一、Huffman编码

霍夫曼(Huffman)树是一类带权路径长度最短的二叉树树。Huffman树的一个非常重要的应用就是进行Huffman编码以得到0-1码流进行快速传输。
在电报收发等数据通讯中,常需要将传送的文字转换成由二进制字符0、1组成的字符串来传输。为了使收发的速度提高,就要求电文编码要尽可能地短。此外,要设计长短不等的编码,还必须保证任意字符的编码都不是另一个字符编码的前缀,目的是解决译码的二义性。
例如:假设有一电文“EABCBAEDBCEEEDCEBABC”,其中的字符集为C={A,B,C,D,E},各个字符出现的次数集为W={3, 5, 4, 2, 6}。
在这里插入图片描述
以字符集C作为叶子结点,次数集W作为结点的权值来构造 Huffman树,则得到如下的Huffman树:
在这里插入图片描述
其中树叶结点就是电文中的字符,树叶中的数字就是对应字符出现的次数。

规定Huffman树中左分支代表“0”,右分支代表“1” ,则得到如下的Huffman树:
在这里插入图片描述
从根结点到每个叶子结点所经历的路径分支上的“0”或“1”所组成的字符串,为该结点所对应的编码,称之为Huffman编码。
各个字符的Huffman编码结果如下:
在这里插入图片描述

由于每个字符都是叶子结点,不可能出现在根结点到其它字符结点的路径上,所以一个字符的Huffman编码不可能是另一个字符的Huffman编码的前缀。

二、Huffman编码算法

1.编码方式
根据出现频度(权值)Weight,对叶子结点的Huffman编码有两种方式:
1)从叶子结点到根逆向处理,求得每个叶子结点对应字符的Huffman编码。
2)从根结点开始遍历整棵二叉树,求得每个叶子结点对应字符的Huffman编码。
2.逆向编码算法
本文以逆向编码为例,给出Huffman编码的算法。
对于每个树叶结点,其Huffman编码算法如下:
Step 1:把树叶作为当前结点,并获取其序号(数组的下标)
Step 2:获取当前结点的双亲结点的序号
Step 3:判断当前结点是其双亲结点的左或者右子树,如果是左,则编码为’0’,否则为’1’
Step 4:如果双亲结点为树根,则结束当前树叶的编码;否则,更新当前结点序号为其双亲的序号,返回Step 2.
:因为是从树叶开始到树根的编码,因此在存储编码的时候是倒序存储,即把当前树叶编码的第一个字符存到数组的最后位置,然后向前依次存储。

三、Huffman编码之C程序

1.Huffman编码算法

/******************************************************************************************
函数:void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC )
功能:对Huffman树的叶子结点进行编码。
说明:Huffman树的结点存储在结构体数组HT里,其中前面WeightNum个元素为叶子结点,存储给定信息
      的权值。
输入参数:WeightNum:权值的个数,也就是Huffman树上叶子结点的个数
          NodeNum:  Huffman树上全部结点的个数
          HT:       存储Huffman树上所有结点的信息,权、双亲编号、左孩子编号、右孩子编号
输出参数:HC:       存储树叶结点的Huffman编码,每个编码均为一个字符串
*******************************************************************************************/
void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC )
{
    int  k, sp, p, fp;
	char *cd;
	cd = new char[ WeightNum + 1 ];    // 动态分配存储编码的工作空间
	cd[ WeightNum ] = '\0';            // 编码的结束标志
	for ( k = 1; k <= WeightNum; k++ ) // 逐个求字符的编码
	{
		sp = WeightNum;  
		//从叶子结点到根逆向求编码
		for( p = k, fp = HT[k].Parent;  fp != 0; p = fp, fp = HT[p].Parent )  
		{
			if  ( HT[fp].Lchild == p )
				cd[ --sp ] = '0';
			else
				cd[ --sp ] = '1';
		}
		HC[k] = new char[ WeightNum - sp + 1 ]; //为第k个字符分配空间 
		strcpy( HC[k], &cd[sp] ) ;
	}
	delete[] cd ;
}

2.完整的测试代码

#include"stdio.h"
#include"string.h"
#define  MAX_NODE  200     //Max_Node>2n-1

//存储Huffman树结点
typedef struct
{
	char Character; //信息字符
	int  Weight;    //权
	int  Parent;    //双亲结点编号
	int  Lchild;    //左孩子结点编号 
	int  Rchild;    //右孩子结点编号
} HTNode ;

//创建一棵叶子结点数为WeightNum的Huffman树,生成Huffman树后,树的根结点的下标是2WeightNum-1
void Create_Huffman( int WeightNum, HTNode *HT, int NodeNum );
//对已经创建的huffman树进行编码
void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC );

int main()
{
	int       WeightNum;//已知的权值的个数,也就是叶子结点个数
	int       NodeNum;  //huffman树上全部结点个数
	HTNode    *HT;      //存储Huffman树的结点
	char      **HC;     //存储Huffman编码
	
	WeightNum = 5;
	NodeNum   = 2 * WeightNum - 1;
	
	//建立Huffman树
	HT = new HTNode[MAX_NODE];
	Create_Huffman( WeightNum, HT, NodeNum );
	//向屏幕输出Huffman树的结点
	printf( "Huffman树上的结点:\n" );
	int i;
	for( i = 1; i <= NodeNum; i++ )
	{
		if( i <= WeightNum )
			printf( "%c %d %d %d %d\n", HT[i].Character, HT[i].Weight, HT[i].Parent, HT[i].Lchild, HT[i].Rchild );
		else
			printf( "%c %d %d %d %d\n", ' ', HT[i].Weight, HT[i].Parent, HT[i].Lchild, HT[i].Rchild );
	}

	//Huffman编码
	HC = new char*[ WeightNum + 1];
	Huff_coding( WeightNum, HT, NodeNum, HC );

	//向屏幕输出Huffman编码
	printf( "Huffman编码:\n" );
	for( i = 1; i <= WeightNum; i++ )
	{
		printf( "%c: %s\n", HT[i].Character, HC[i] );
	}

	delete[] HT;
	delete[] HC;
	return 0;
}
/******************************************************************************************
函数:void Create_Huffman( int WeightNum, HTNode *HT, int NodeNum )
功能:对给定的权值,生成Huffman树
说明:Huffman树的结点存储在结构体数组HT里,其中前面WeightNum个元素为叶子结点,存储给定信息
      的权值。
输入参数:WeightNum:权值的个数,也就是Huffman树上叶子结点的个数
          NodeNum:  Huffman树上全部结点的个数
输出参数:HT:       存储Huffman树上所有结点的信息,权、双亲编号、左孩子编号、右孩子编号
*******************************************************************************************/
void Create_Huffman( int WeightNum, HTNode *HT, int NodeNum )
{   
	int  k , j;   //循环下标
	int  w1, w2;  //w1 , w2分别保存权值最小的两个权值     
	int  p1, p2;  //p1 , p2保存两个最小权值的下标 
	
	for ( k = 1;  k <= NodeNum;  k++ )  //step1:初始化向量HT,即所有成员均当做树根
	{   
		if( k <= WeightNum ) //输入时,所有叶子结点都有权值
		{ 
			printf( "Please Input Character : Character =?" );
			scanf( "%c", &HT[k].Character );   
			printf( "Please Input Weight : Weight =?" );
			scanf( "%d", &HT[k].Weight );     
			getchar(); //过滤输入数据时的换行符
		}  
		else  
			HT[k].Weight = 0;  //非叶子结点没有权值
		HT[k].Parent = HT[k].Lchild = HT[k].Rchild = 0 ;
	}
	for( k = WeightNum + 1; k <= NodeNum; k++ )//step2:对非叶子节点赋值,以生成H树
	{ 
		p1 = 0;
		p2 = 0;
		w1 = 0xFFFFFFF;
		w2 = w1;
		for( j = 1 ; j <= k-1 ; j++)  //找到权值最小的两个值及其下标
		{
			if( HT[j].Parent == 0 )    //尚未合并 
			{
				if( HT[j].Weight < w1 )
				{
					w2 = w1; 
					p2 = p1;
					w1 = HT[j].Weight;   
					p1 = j;  
				}
				else if( HT[j].Weight < w2 )
				{
					w2 = HT[j].Weight;  
					p2 = j;   
				}
			}      
		} 
		HT[k].Lchild  = p1;    
		HT[k].Rchild  = p2;
		HT[k].Weight  = w1 + w2;
		HT[p1].Parent = k; 
		HT[p2].Parent = k; 
    }
}
/******************************************************************************************
函数:void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC )
功能:对Huffman树的叶子结点进行编码。
说明:Huffman树的结点存储在结构体数组HT里,其中前面WeightNum个元素为叶子结点,存储给定信息
      的权值。
输入参数:WeightNum:权值的个数,也就是Huffman树上叶子结点的个数
          NodeNum:  Huffman树上全部结点的个数
          HT:       存储Huffman树上所有结点的信息,权、双亲编号、左孩子编号、右孩子编号
输出参数:HC:       存储树叶结点的Huffman编码,每个编码均为一个字符串
*******************************************************************************************/
void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC )
{
    int  k, sp, p, fp;
	char *cd;
	cd = new char[ WeightNum + 1 ];    // 动态分配存储编码的工作空间
	cd[ WeightNum ] = '\0';            // 编码的结束标志
	for ( k = 1; k <= WeightNum; k++ ) // 逐个求字符的编码
	{
		sp = WeightNum;  
		//从叶子结点到根逆向求编码
		for( p = k, fp = HT[k].Parent;  fp != 0; p = fp, fp = HT[p].Parent )  
		{
			if  ( HT[fp].Lchild == p )
				cd[ --sp ] = '0';
			else
				cd[ --sp ] = '1';
		}
		HC[k] = new char[ WeightNum - sp + 1 ]; //为第k个字符分配空间 
		strcpy( HC[k], &cd[sp] ) ;
	}
	delete[] cd ;
}

3.测试结果
在这里插入图片描述

  • 17
    点赞
  • 103
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Huffman编码是一种基于概率的无损数据压缩算法,可以用于压缩各种类型的数据,包括图片。下面是一个简单的C语言实现Huffman编码图片压缩的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_TREE_HT 100 typedef struct node { unsigned char data; unsigned freq; struct node *left, *right; } node; typedef struct heap { unsigned size; unsigned capacity; node **array; } heap; typedef struct huff_code { unsigned char data; char *code; } huff_code; node *new_node(unsigned char data, unsigned freq) { node *temp = (node *)malloc(sizeof(node)); temp->left = temp->right = NULL; temp->data = data; temp->freq = freq; return temp; } heap *new_heap(unsigned capacity) { heap *temp = (heap *)malloc(sizeof(heap)); temp->size = 0; temp->capacity = capacity; temp->array = (node **)malloc(capacity * sizeof(node *)); return temp; } void swap_node(node **a, node **b) { node *temp = *a; *a = *b; *b = temp; } void min_heapify(heap *h, int idx) { int smallest = idx; int left = 2 * idx + 1; int right = 2 * idx + 2; if (left < h->size && h->array[left]->freq < h->array[smallest]->freq) smallest = left; if (right < h->size && h->array[right]->freq < h->array[smallest]->freq) smallest = right; if (smallest != idx) { swap_node(&h->array[smallest], &h->array[idx]); min_heapify(h, smallest); } } node *extract_min(heap *h) { node *temp = h->array[0]; h->array[0] = h->array[h->size - 1]; --h->size; min_heapify(h, 0); return temp; } void insert_heap(heap *h, node *temp) { ++h->size; int i = h->size - 1; while (i && temp->freq < h->array[(i - 1) / 2]->freq) { h->array[i] = h->array[(i - 1) / 2]; i = (i - 1) / 2; } h->array[i] = temp; } void build_heap(heap *h) { int n = h->size - 1; int i; for (i = (n - 1) / 2; i >= 0; --i) min_heapify(h, i); } int is_leaf(node *root) { return !(root->left) && !(root->right); } heap *create_heap(char *data, int *freq, int size) { heap *h = new_heap(size); int i; for (i = 0; i < size; ++i) h->array[i] = new_node(data[i], freq[i]); h->size = size; build_heap(h); return h; } node *build_huffman_tree(char *data, int *freq, int size) { node *left, *right, *top; heap *h = create_heap(data, freq, size); while (h->size > 1) { left = extract_min(h); right = extract_min(h); top = new_node('$', left->freq + right->freq); top->left = left; top->right = right; insert_heap(h, top); } return extract_min(h); } void print_codes(node *root, char *code, int top, huff_code *h_codes) { if (root->left) { code[top] = '0'; print_codes(root->left, code, top + 1, h_codes); } if (root->right) { code[top] = '1'; print_codes(root->right, code, top + 1, h_codes); } if (is_leaf(root)) { h_codes[root->data].data = root->data; h_codes[root->data].code = (char *)malloc((top + 1) * sizeof(char)); memcpy(h_codes[root->data].code, code, top); h_codes[root->data].code[top] = '\0'; } } void encode_image(char *image_path, huff_code *h_codes) { FILE *fp = fopen(image_path, "rb"); if (!fp) { printf("Error opening file %s\n", image_path); exit(1); } fseek(fp, 0L, SEEK_END); int file_size = ftell(fp); fseek(fp, 0L, SEEK_SET); char *image_data = (char *)malloc(file_size * sizeof(char)); fread(image_data, sizeof(char), file_size, fp); fclose(fp); int freq[256] = {0}; int i; for (i = 0; i < file_size; ++i) ++freq[(unsigned char)image_data[i]]; node *root = build_huffman_tree(image_data, freq, 256); char code[MAX_TREE_HT]; print_codes(root, code, 0, h_codes); } void write_codes_to_file(huff_code *h_codes, char *output_file) { FILE *fp = fopen(output_file, "wb"); if (!fp) { printf("Error opening file %s\n", output_file); exit(1); } int i; fwrite(&h_codes[0].data, sizeof(unsigned char), 1, fp); for (i = 0; i < 256; ++i) { if (h_codes[i].code) { fwrite(&h_codes[i].data, sizeof(unsigned char), 1, fp); unsigned char code_len = strlen(h_codes[i].code); fwrite(&code_len, sizeof(unsigned char), 1, fp); fwrite(h_codes[i].code, sizeof(char), strlen(h_codes[i].code), fp); } } fclose(fp); } int main(int argc, char *argv[]) { char *image_path = argv[1]; char *output_file = argv[2]; huff_code h_codes[256] = {0}; encode_image(image_path, h_codes); write_codes_to_file(h_codes, output_file); return 0; } ``` 该代码实现了将指定路径的图片进行Huffman编码压缩,并将压缩后的编码写入指定的输出文件中。其中,`encode_image()`函数用于对图片进行编码,`write_codes_to_file()`函数用于将编码写入文件中。需要注意的是,该代码只实现了压缩部分,解压缩部分需要另外实现。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值