一、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';elsecd[ --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 );elseprintf( "%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';elsecd[ --sp ] = '1';}HC[k] = new char[ WeightNum - sp + 1 ]; //为第k个字符分配空间 strcpy( HC[k], &cd[sp] ) ;}delete[] cd ;
}

3.测试结果

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

  1. 选择排序算法详解之C语言版

    一.算法原理 选择排序属于不稳定排序法,是一种常用的排序算法,其时间复杂度为O(n^2). 所谓的不稳定排序算法是指在一组数据中存在多个相同的数据,但是在排序之后,相同数据的前后位置会发生改变.例如有 ...

  2. 计算机操作系统——银行家算法详解(C语言版)

    目录 一.实验目的 二.实验内容 三.实验要点说明 数据结构 银行家算法bank()函数 安全性算法safe()函数 银行家算法实例 程序结构 四.实验代码 五.结果展示 一.实验目的 通过编写一个模 ...

  3. 哈希(Hash)查找算法详解之C语言版

    一.哈希查找算法原理 哈希查找是一种快速查找算法,该算法不需要对关键字进行比较,而是以关键字为自变量,以该关键字在存储空间中的地址为因变量,建立某种函数关系,称为哈希函数,这样在查找某一关键字的时候, ...

  4. 折半插入排序算法详解之C语言版

    一.算法原理 折半插入排序是插入排序方法中一种,相比较与直接插入排序算法,减少了排序过程中比较次数,也是一种常用的排序算法. 折半插入排序算法基本原理是将折半查找方法与直接插入排序方法相结合,也就是在 ...

  5. 基于霍夫曼(Huffman)图像编码的图像压缩和重建-含Matlab代码

    目录 一.引言 二.霍夫曼Huffman编码 2.1 霍夫曼编码流程 2.2 输入数据的编码 三.霍夫曼解码 四.实验结果 五.参考文献 六.Matlab代码(GUI界面)获取 一.引言 随着通信与信 ...

  6. Huffman 编码原理详解(代码示例)

    1.概述 huffman编码是一种可变长编码(  VLC:variable length coding))方式,于1952年由huffman提出.依据字符在需要编码文件中出现的概率提供对字符的唯一编码 ...

  7. 隐马尔可夫模型之Baum-Welch算法详解

    隐马尔可夫模型之Baum-Welch算法详解 前言 在上篇博文中,我们学习了隐马尔可夫模型的概率计算问题和预测问题,但正当要准备理解学习问题时,发现学习问题中需要EM算法的相关知识,因此,上一周转而学 ...

  8. 图之邻接矩阵详解(C语言版)

    文章目录 一.定义 二.结构 三.常用操作 结语 附录 一.定义 图的邻接矩阵是一种采用邻接矩阵数组表示顶点之间相邻关系的存储结构.设图G有n个顶点,则邻接矩阵是一个n*n的方阵,定义为:       ...

  9. 图遍历详解(C语言版)

    文章目录 一.定义 二.方法 1.深度优先遍历 2.广度优先遍历 三.实现 1.无向图或强连通有向图遍历 2.非连通图遍历 结语 附录 一.定义 从给定图中任意指定的顶点(称为初始点)出发,按照某种搜 ...

最新文章

  1. (用微信扫的静态链接二维码)微信native支付模式官方提供的demo文件中的几个bug修正...
  2. 自学python之路(day2)
  3. LOJ #6052. 「雅礼集训 2017 Day11」DIV
  4. 队列的基础概念与经典题目(Leetcode题解-Python语言)
  5. Sentinel(九)之热点参数限流
  6. 小学教师计算机说课,浙江温州小学计算机教师资格认证说课稿
  7. ubuntu之安装显卡驱动
  8. 关于安装TOMCAT解压版环境配置流程
  9. io_uring 新异步 IO 机制,性能提升超 150%,堪比 SPDK
  10. jQuery操作input值总结
  11. opera档案学习(二)
  12. QIIME 2教程. 22命令行界面q2cli(2021.2)
  13. 捷联惯导系统学习6.13(状态估计的误差分配与可观测度分析 )
  14. sbit在c语言中作用,sbit在单片机中的表示和作用?
  15. 网站访问流程及原理分析
  16. vivado生成bit流错误:Combinatorial Loop Alert
  17. UI基础一:简单的BOL查询
  18. 南京工业大学校园网(智慧南工)自动登录
  19. 气体灭火系统的发展历程
  20. matlab+字体设置大小,Matlab中如何修改字体的大小?

热门文章

  1. 20190703 windows 右下角系统托盘直接显示电量数字百分比
  2. 华为mate8电池虚电校正_vivo手机虚电量如何进行电池校正
  3. java-net-php-python-springboo水果外卖销售管理系统计算机毕业设计程序
  4. 31 正项级数敛散性判别法
  5. 无约束正项式几何规划
  6. 优酷视频上传工具可以试用吗
  7. php is_null和empty,php empty,isset,is_null判断比较(差异与异同)
  8. MATLAB制作动图或视频
  9. 花朵数c语言算法,画几朵花
  10. 记录一次mac 外接硬盘安装win10的过程