哈夫曼编码的实现及多进制的扩展

目录

前言

一、哈夫曼编码的来源

二、编码过程

三、编码流程图及案例

1.三进制的哈夫曼编码

2.四进制的哈夫曼编码

四、程序代码

五、程序样例


前言

哈夫曼编码(Huffman Coding),又称霍夫曼编码,是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。

一、哈夫曼编码的来源

哈夫曼编码的由来可以追溯到1950年代初,由大卫·哈夫曼(David A. Huffman)于1952年在他的博士论文中提出。当时,大卫·哈夫曼是麻省理工学院(MIT)的研究生,在为信息论研究编码问题时,他开始探索一种能够实现高效数据压缩的编码方法。


在传统的固定长度编码中,每个符号(如字母、数字等)都分配固定长度的位模式,不管其出现频率如何。然而,大卫·哈夫曼认识到,对于不同出现频率的符号,使用固定长度编码并不是最有效的方式,因为较低频率的符号被分配了与高频率符号相同长度的编码,导致整体编码长度较长。


基于这一认识,大卫·哈夫曼提出了一种新的变长编码方案,即哈夫曼编码。他的目标是通过构建一棵树来分配较短的编码给高频率的符号,并分配较长的编码给低频率的符号,从而实现更高效的数据压缩。


在他的研究中,大卫·哈夫曼首先提出了一种构建哈夫曼树的贪心算法,该算法通过反复合并出现频率最低的符号,构建出一棵树,并为每个符号分配唯一的哈夫曼编码。这种编码方案保证了没有任何一个编码是其他编码的前缀,从而编码是无歧义的。


大卫·哈夫曼的贡献被广泛认可,哈夫曼编码成为了一种重要的数据压缩技术,并广泛应用于图像、音频、视频以及通信等领域。它的简单性、高效性和无损性使得哈夫曼编码成为了数据压缩领域的核心算法之一。

二、编码过程

哈夫曼编码(Huffman Coding)是一种广泛应用于数据压缩的无损编码算法。它通过构建一棵哈夫曼树(Huffman Tree)来实现对源数据的编码。哈夫曼树的叶子节点对应于源数据中的字符,其编码过程如下:

1. 统计字符频率:首先,统计源数据中每个字符的出现频率。频率越高的字符对编码长度的影响越大。

2. 创建哈夫曼树:根据字符频率,构建一棵哈夫曼树。构建过程如下:
a. 将每个字符看作一个节点,节点的权重为其出现频率。
b. 选取权重最小的两个节点,将其合并为一个新的节点,新节点的权重为两个子节点权重之和。
c. 将新节点放回节点集合中,重复步骤 b,直到节点集合中只剩下一个节点。这个节点即为哈夫曼树的根节点。

3.生成编码表:从哈夫曼树的根节点开始,向左走的路径上的边标记为 0,向右走的路径上的边标记为 1。每个叶子节点的 0/1 标记序列即为该字符的哈夫曼编码。

4. 编码数据:将源数据中的每个字符替换为其对应的哈夫曼编码,从而得到压缩后的数据。

解码过程:在解码时,从哈夫曼树的根节点开始,按照二进制编码向左或向右遍历树,直到到达叶子节点。将遍历过程中遇到的字符按照顺序拼接,即可还原出原始数据。

哈夫曼编码是一种变长编码,出现频率高的字符使用较短的编码,出现频率低的字符使用较长的编码。这种编码方式的优势在于它能有效减少数据存储空间,提高数据传输效率。

三、编码流程图

1.三进制的哈夫曼编码

(1)将信源消息符号按其出现的概率大小依次排列为p1≥p2≥…≥pn

(2)取个概率最小的字母分别配以012三个码元,并将这个概率相加作为一个新字母的概率,与未分配进符号的字母一起重新排队。

(3)对重排后的个概率最小符号重复步骤(2)的过程。

(4)不断继续上述过程,直到最后个符号配以012为止。

(5)从最后一级开始,向前返回得到各个信源符号所对应的码元序列,即相应的码字。

 

2.四进制的哈夫曼编码

(1)将信源消息符号按其出现的概率大小依次排列为p1≥p2≥…≥pn

(2)取个概率最小的字母分别配码元0,1,2,3,4,并将这个概率相加作为一个新字母的概率,与未分配进符号的字母一起重新排队。

(3)对重排后的个概率最小符号重复步骤(2)的过程。

(4)不断继续上述过程,直到最后两个符号配以四个码元为止。

(5)从最后一级开始,向前返回得到各个信源符号所对应的码元序列,即相应的码字。

四、程序代码

代码如下:

#include"stdio.h"
#include"string.h"
#define  MAX_NODE  200     

typedef struct
{
	char Character; 
	int  Weight;    
	int  Parent;    
	int  Lchild;     
	int  Rchild;    
} HTNode ;

void Create_Huffman( int WeightNum, HTNode *HT, int NodeNum );
void Huff_coding( int WeightNum, HTNode *HT, int NodeNum, char **HC );

int main()
{
	int       WeightNum;//已知的权值的个数,也就是叶子结点个数
	int       NodeNum;  //哈夫曼树上全部结点个数
	HTNode    *HT;      //存储哈夫曼树的结点
	char      **HC;     //存储哈夫曼编码
	
	WeightNum = 5;
	NodeNum   = 2 * WeightNum - 1;
	
	HT = new HTNode[MAX_NODE];
	Create_Huffman( WeightNum, HT, NodeNum );
	printf( "哈夫曼树上的结点:\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 );
	}

	HC = new char*[ WeightNum + 1];
	Huff_coding( WeightNum, HT, NodeNum, HC );
	printf( "哈夫曼编码:\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 )
{   
	int  k , j;   //循环下标
	int  w1, w2;  //w1 , w2分别保存权值最小的两个权值     
	int  p1, p2;  //p1 , p2保存两个最小权值的下标 
	
	for ( k = 1;  k <= NodeNum;  k++ )  //初始化向量HT,即所有成员均当做树根
	{   
		if( k <= WeightNum ) //输入时,所有叶子结点都有权值
		{ 
			printf( "Please Input Character : ?" );
			scanf( "%c", &HT[k].Character );   
			printf( "Please Input 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++ )//对非叶子节点赋值,以生成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 )
{
    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 ;
}


五、程序样例

以上就是本组所研究的内容,本文介绍了哈夫曼编码的使用,而哈夫曼编码可以有效减少数据存储空间,提高数据传输效率。

小组成员:P02114165于欣雅、P01114168毕薇、P02114170叶斌、P02114172纪雨晴、P02114174徐峰涛、P02114176刘馨。

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值