赫夫曼树(Huffman Tree),又称最优二叉树,是一类带权路径长度最短的树。假设有n个权值{w1,w2,…,wn},如果构造一棵有n个叶子节点的二叉树,而这n个叶子节点的权值是{w1,w2,…,wn},则所构造出的带权路径长度最小的二叉树就被称为赫夫曼树。

树的带权路径长度指树中所有叶子节点到根节点的路径长度与该叶子节点权值的乘积之和,如果在一棵二叉树中共有n个叶子节点,用Wi表示第i个叶子节点的权值,Li表示第i个也叶子节点到根节点的路径长度,则该二叉树的带权路径长度 WPL=W1 x L1 + W2 x L2 + … Wn x Ln。

根据节点的个数以及权值的不同,赫夫曼树的形状也各不相同,赫夫曼树具有如下特性:

  1. 对于同一组权值,所能得到的赫夫曼树不一定是唯一的。
  2. 赫夫曼树的左右子树可以互换,因为这并不影响树的带权路径长度。
  3. 带权值的节点都是叶子节点,不带权值的节点都是某棵子二叉树的根节点。
  4. 权值越大的节点越靠近赫夫曼树的根节点,权值越小的节点越远离赫夫曼树的根节点。
  5. 赫夫曼树中只有叶子节点和度为2的节点,没有度为1的节点。
  6. 一棵有n个叶子节点的赫夫曼树共有2n-1个节点。

赫夫曼树的构建步骤如下:

  1. 将给定的n个权值看做n棵只有根节点(无左右孩子)的二叉树,组成一个集合HT,每棵树的权值为该节点的权值。
  2. 从集合HT中选出2棵权值最小的二叉树,组成一棵新的二叉树,其权值为这2棵二叉树的权值之和。
  3. 将步骤2中选出的2棵二叉树从集合HT中删去,同时将步骤2中新得到的二叉树加入到集合HT中。
  4. 重复步骤2和步骤3,直到集合HT中只含一棵树,这棵树便是赫夫曼树。

假如给定如下5个权值:

则按照以上步骤,可以构造出如下面左图所示的赫夫曼树,当然也可能构造出如下面右图所示的赫夫曼树,这并不是唯一的。

Huffman编码

赫夫曼树的应用十分广泛,比如众所周知的在通信电文中的应用。在等传送电文时,我们希望电文的总长尽可能短,因此可以对每个字符设计长度不等的编码,让电文中出现较多的字符采用尽可能短的编码。为了保证在译码时不出现歧义,我们可以采取如下图所示的编码方式

即左分支编码为字符0,右分支编码为字符1,将从根节点到叶子节点的路径上分支字符组成的字符串作为叶子节点字符的编码,这便是赫夫曼编码。我们根据上面左图可以得到各叶子节点的赫夫曼编码如下:

权值为5的也自己节点的赫夫曼编码为:11

权值为4的也自己节点的赫夫曼编码为:10

权值为3的也自己节点的赫夫曼编码为:00

权值为2的也自己节点的赫夫曼编码为:011

权值为1的也自己节点的赫夫曼编码为:010

代码实现:

#include<iostream>
using namespace std;  //单独的命名空间
#define maxSize 100
#pragma warning(disable:4996)//哈弗曼树的存储结构
typedef struct Node {int weight;   //权值int parents;   //父结点int lchild;    //左节点int rchild;    //右节点
}HTNode, *HuffmanTree;//哈夫曼编码的存储结构
typedef char **HuffmanCode; //声明
HuffmanTree create_HuffmanTree(int *wet, int n);
void select_minium(HuffmanTree HT, int k, int &min1, int &min2);
int min(HuffmanTree HT, int k);
void HuffmanCoding(HuffmanTree HT, HuffmanCode &HC, int n);//构造哈弗曼树
HuffmanTree create_HuffmanTree(int *wet, int n) {//wet:权值,n:数量//度为0的节点数为n,度为1的节点数为n-1int total = 2 * n - 1;//所有用到的节点数//申请空间,空间类型为HuffmanTree,空间大小为total * sizeof(HTNode)HuffmanTree HT = (HuffmanTree) malloc(total * sizeof(HTNode)); //判断空间是否申请成功if (!HT) {cout << "huffmantree malloc failed" << endl;exit(-1);}//哈夫曼树前n个节点的初始化int i;for ( i = 0; i < n; i++){HT[i].parents = -1;HT[i].lchild = -1;HT[i].rchild = -1;HT[i].weight = *wet;wet++;}//度为1的节点初始化for (int i = n ; i < total; i++){HT[i].parents = -1;HT[i].lchild = -1;HT[i].rchild = -1;HT[i].weight = 0;}//假设选出两个权值最小节点int min1, min2;  //用来保存每一轮选出的两个weight最小且parent为0的节点//每一轮比较后选择出min1和min2构成一棵二叉树,最后构成一棵霍夫曼树for ( i = n; i < total; i++){select_minium(HT,i, min1, min2);HT[min1].parents = i;HT[min2].parents = i;//哈弗曼树的做右孩子可以反过来,只是所得的编码不同。HT[i].lchild = min1;HT[i].rchild = min2;HT[i].weight = HT[min1].weight + HT[min2].weight;}return HT;}//从HT数组的前k个元素选出weight最小且parent为-1的两个,分别将其序号保存在min1和min2中
void select_minium(HuffmanTree HT,int k,int &min1,int &min2) {min1 = min(HT, k);min2 = min(HT, k);
}// 从HT数组的前k个元素中,选出weight最小且parent为-1的元素,并返回元素序号
int min(HuffmanTree HT, int k) {int i = 0;int min = 0;//用来存放weight最小且parent为-1的元素的序号int min_weight;//用来存放weight最小且parent为-1的元素的weight值。//先将第一个parent为-1的元素的weight值赋给min_weight,留作以后比较用//注意,这里不能按照一般的做法,先直接将HT[0].weight赋给min_weight,//因为如果如果HT[0].weight的值比较小,那么在第一次构造二叉树时就会被选走,//而后续的每一轮选择最小权值构造二叉树的比较还是先用HT[0].weight的值来进行判断,//这样又会再次将其选走,从而产生逻辑上的错误。while (HT[i].parents != -1)i++;min_weight = HT[i].weight;min = i;//选出weight最小且parent为-1的元素,并将其序号赋给minfor ( ; i < k; i++){if ( HT[i].weight < min_weight && HT[i].parents == -1) {min_weight = HT[i].weight;min = i;}}//选出weight最小的元素后,将其parent置为1,使得下一次比较时将其排除在外HT[min].parents = 1;return min; //返回最小值}//采用从叶子节点到根节点逆向求霍夫曼树HT中n个叶子节点的霍夫曼编码,并保存在HC中
void HuffmanCoding(HuffmanTree HT, HuffmanCode &HC, int n) {//申请空间,用来保存指向每个霍夫曼编码串的指针HC = (HuffmanCode)malloc(n * sizeof(char *));if (!HT){cout << "huffmancode mallioc fialed" << endl;}//申请空间,用来保存每次求得的霍夫曼编码串//对于有n个叶子结点的霍夫曼树,各个叶子结点的编码长度最长不超过n-1char *code = (char *)malloc(n * sizeof(char *));if (!code){cout << "code malloc failed" << endl;exit(-1);}//code[n - 1] = '\0';//编码结束符int cur = 2 * n - 2;int code_len = 0;//访问状态 weight 0:全未被访问   weight 1:左节点被访问   weight 2:所有节点被访问int i;for (i = 0; i < cur + 1; i++) {HT[i].weight = 0;//访问状态初始化}//从根节点开始进行一个整体的访问while (cur != -1) {if (HT[cur].weight == 0) {//说明所有节点都未被访问HT[cur].weight = 1;if (HT[cur].lchild != -1) {//判断左孩子是不是叶节点code[code_len++] = '0';cur = HT[cur].lchild;}else{code[code_len] = '\0';//为第cur个字符的编码串分配存储空间HC[cur] = (char *) malloc((code_len + 1) * sizeof(char));if (!HC[cur]){cout << "HC[cur] malloc failed" << endl;exit(-1);}//将编码串从code复制到HCstrcpy(HC[cur],code);}}else if (HT[cur].weight == 1) {// 说明左节点被访问HT[cur].weight = 2;//将访问状态改变if (HT[cur].rchild != -1) {//如果HT[cur].rchild不是叶节点code[code_len++] = '1';cur = HT[cur].rchild;}}else {HT[cur].weight = 0;cur = HT[cur].parents;--code_len;}}for (int j = 0; j < n; ++j) {cout << HC[j] << endl;}free(code);}//测试int main() {int w[] = { 1,2,3,4,5 };//权值int n = 5;//节点个数HuffmanCode HC = NULL;HuffmanTree hTree = create_HuffmanTree(w, n);HuffmanCoding(hTree, HC, n);return 0;
}

Huffman编码C实现相关推荐

  1. huffman树和huffman编码

    不知道为什么,我写的代码都是又臭又长. 直接上代码: #include <iostream> #include <cstdarg> using namespace std; c ...

  2. 技术图文:如何利用C#实现Huffman编码?

    背景 Huffman编码 在通信和数据压缩领域具有重要的应用. 在介绍 Huffman 编码具体实现之前,先介绍几个相关的概念. 概念1:树中结点的带权路径长度 – 根结点到该结点的路径长度与该结点权 ...

  3. huffman java_详解Huffman编码算法之Java实现

    Huffman编码介绍 Huffman编码处理的是字符以及字符对应的二进制的编码配对问题,分为编码和解码,目的是压缩字符对应的二进制数据长度.我们知道字符存贮和传输的时候都是二进制的(计算机只认识0/ ...

  4. Huffman 编码压缩算法

    为什么80%的码农都做不了架构师?>>>    前两天发布那个rsync算法后,想看看数据压缩的算法,知道一个经典的压缩算法Huffman算法.相信大家应该听说过 David Huf ...

  5. 采用Huffman编码进行数据压缩

    文章目录 问题 实验环境 程序组成 实现思路 如何用二进制0/1表示字符 '0' / '1' 源代码下载 程序运行和结果: 总结 问题 利用哈夫曼编码将英文文献进行压缩 注:哈夫曼算法及原理见博客ht ...

  6. huffman编码压缩算法

    转自:http://coolshell.cn/articles/7459.html 前两天发布那个rsync算法后,想看看数据压缩的算法,知道一个经典的压缩算法Huffman算法.相信大家应该听说过  ...

  7. 数据结构实验三:Huffman树及Huffman编码的算法实现

    Exp03 Huffman树及Huffman编码的算法实现 Author: Maskros 实验目的 了解该树的应用实例,熟悉掌握Huffman树的构造方法及Huffman编码的应用, 了解Huffm ...

  8. Huffman编码(Huffman树)

    [0]README 0.1) 本文总结于 数据结构与算法分析, 源代码均为原创, 旨在 理解 "Huffman编码(Huffman树)" 的idea 并用源代码加以实现: 0.2) ...

  9. huffman编码的程序流程图_Huffman编码实现压缩解压缩

    这是我们的课程中布置的作业.找一些资料将作业完毕,顺便将其写到博客,以后看起来也方便. 原理介绍 什么是Huffman压缩 Huffman( 哈夫曼 ) 算法在上世纪五十年代初提出来了,它是一种无损压 ...

  10. 文件压缩 Huffman编码 (java)

    用到的知识点有: 1,二叉堆Heap的设计,实现堆排序 2,二叉树的链式构造 3,Huffman编码以及Huffman树的构建 4,二进制文件I/O流 的读写 5,比特输出流的类设计 程序目录: 1 ...

最新文章

  1. 百度ueditor编辑器动态获取项目根目录
  2. 两万字深度介绍分布式系统原理,一文入魂
  3. [转载] 杜拉拉升职记——15 1001个笑话
  4. mysql 性能 比较好_MySQL性能优化的最佳20+条经验
  5. 验证手机号 身份证 中文名称
  6. 为网格布局图片打造的超炫 CSS 加载动画
  7. __syncthreads()
  8. 计科14-1 140201125 王振禹 作业三
  9. 光凭 5G 根本无法解决宽带问题!
  10. Spring Cloud Alibaba Nacos之服务配置中心
  11. 玩游戏提示计算机内存不足,Win10玩吃鸡游戏提示“虚拟内存不足”怎么办?
  12. mysql存储视频_特殊格式文件(视频、声音等) 在数据库中的存储方式
  13. jsp异常 The JSP specification requires that an attribute name is preceded by whitespace
  14. python实现一个web服务器
  15. 浅谈iOS中关于app的优化
  16. VL53L0X 底层思路整理(1)
  17. oracle sysdate 月份,如何将oracle中的sysdate月份值转换为数字?
  18. ORACLE给指定用户授权表的部分权限
  19. LoRa学习:SX127x芯片数字IO引脚映射
  20. Minecraft 1.16.5 生化8 模组 1.8版本 版本同步+支持服务器联机

热门文章

  1. python dropna()用法
  2. WampServer2.0i.exe可正常安装ECShop_V2.7.2 utf8
  3. vue项目实战-4.前端渲染.微信小程序
  4. BASIC 之父出生 | 历史上的今天
  5. IDEA报错:前言中不允许有内容
  6. 3.3 使用广播信道的数据链路层
  7. c语言define存储在哪里,深入理解C语言的define
  8. Java Servlet 非英文乱码
  9. NCBI上测序数据的下载
  10. 红米note9和红米note9s有什么区别