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

这篇具有很好参考价值的文章主要介绍了霍夫曼(Huffman)编码算法详解之C语言版。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、Huffman编码

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

规定Huffman树中左分支代表“0”,右分支代表“1” ,则得到如下的Huffman树:
霍夫曼(Huffman)编码算法详解之C语言版
从根结点到每个叶子结点所经历的路径分支上的“0”或“1”所组成的字符串,为该结点所对应的编码,称之为Huffman编码。
各个字符的Huffman编码结果如下:
霍夫曼(Huffman)编码算法详解之C语言版

由于每个字符都是叶子结点,不可能出现在根结点到其它字符结点的路径上,所以一个字符的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.测试结果
霍夫曼(Huffman)编码算法详解之C语言版文章来源地址https://www.toymoban.com/news/detail-467396.html

到了这里,关于霍夫曼(Huffman)编码算法详解之C语言版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【贪心法】最优前缀码(Huffman哈夫曼算法)

    在计算机中需要用0-1字符串作为代码来表示信息,为了正确解码,必须要求任何字符的代码不能作为其他字符代码的前缀,这样的码称为 二元前缀码。 二元前缀码的存储通常采用二叉树结构,令每个字符作为树叶,对应这个字符的前缀码看作根到这片树叶的一条路径,规定

    2024年02月03日
    浏览(47)
  • C语言---数据结构实验---哈夫曼树及哈夫曼编码的算法实现---图的基本操作

    本篇实验代码非本人写,代码源自外部,经调试解决了部分warning和error后在本地vs上可以正常运行,如有运行失败可换至vs 未来会重构实现该两个实验 内容要求: 1、初始化(Init):能够对输入的任意长度的字符串s进行统计,统计每个字符的频度,并建立哈夫曼树 2、建立编码

    2024年02月13日
    浏览(53)
  • 【数据结构与算法】Huffman编码/译码(C/C++)

    利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道(即可以双向传输信息的信道),每端都需要一个完整的编/译码系

    2024年02月04日
    浏览(44)
  • 哈夫曼树(Huffman Tree)

    哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树。所谓树的带权路径长度,就是树中所有的叶结点的权值乘上其到根结点的路径长度(若根结点为0层,叶结点到根结点的路径长度为叶结点的层数)。树的路径长度是从树根到每一结点的路径长度之和,记为WPL=(

    2024年02月08日
    浏览(35)
  • 哈夫曼树,哈夫曼编码及解码详解

    🌍新人小白的博客 ⌛️希望大家多多关注 🌱一起加油,共同成长 🎃以后会经常更新哒~🙈 ⭐️个人主页: 收藏加关注,永远不迷路~⭐️ 一: 顺序表的操作,你真的学会了吗? 二: 顺序栈的基本操作 三: 循环队列的基本操作,你学会了吗? 四: 单链表的操作(超详细

    2024年02月05日
    浏览(45)
  • 哈夫曼树详解及其应用(哈夫曼编码)

    一,哈夫曼树的基本概念 路径: 从树中一个结点到另一个结点之间的 分支 构成这两个结点间的路径 结点的路径长度 :两结点之间路径上的 分支数 树的路径长度: 从 树根 到每一个结点的 路径长度之和 . 记作:TL 权(weight): 将树中结点赋给一个有着某种含义的数值

    2024年02月04日
    浏览(48)
  • 经典算法: 哈夫曼编码

    从 A 结点到 B 结点所经过的分支序列叫做从 A 结点到 B 结点的 路径; 从 A 结点到 B 结点所经过的分支个数叫做从 A 结点到 B 结点的 路径长度; 从二叉树的根结点到二叉树中所有叶结点的路径长度之和称作 该二叉树的路径长度。 设二叉树有 n 个带权值的叶结点,定义从二叉

    2024年02月06日
    浏览(38)
  • 实现哈夫曼编码(C语言)

    编译环境:Dev-C++ 实现哈夫曼编码的贪心算法实现,并分析哈夫曼编码的算法复杂度。         该题目求最小编码长度,即求最优解的问题,可分成几个步骤,一般来说, 每个步骤的最优解不一定是整个问题的最优解,然而对于有些问题,局部贪心可以得到全局的最优解

    2023年04月20日
    浏览(37)
  • 算法 -汉诺塔,哈夫曼编码

    有三个柱子,分别为 from、buffer、to。需要将 from 上的圆盘全部移动到 to 上,并且要保证小圆盘始终在大圆盘上。 这是一个经典的递归问题,分为三步求解: ① 将 n-1 个圆盘从 from - buffer ② 将 1 个圆盘从 from - to    ③ 将 n-1 个圆盘从 buffer - to   如果只有一个圆盘,那么只需

    2024年02月10日
    浏览(47)
  • 【数据结构--哈夫曼编码(C语言版)】

    哈夫曼树 介绍哈夫曼树前先介绍下面几个名词: 1. 结点的路径长度 l 从根结点到该结点的路径上分支的数目 ,如下图结点 a 的 l = 3 。 2. 树的路径长度 树中所有叶子结点的路径长度之和 ,如下图该树的路径长度为 2 + 3 + 3 + 2 + 2 。 3. 结点的权 w 给每一个结点赋予一个新的数

    2024年02月04日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包