在传输文字时,经常要将文字转换成二进制字符串。所以我们希望编码最短,但是又想保证它的唯一性。哈夫曼树具有最小带权路径长度,用来实现编码就可以编码最短,所以用哈夫曼树来构造编码。而前缀编码就可以保证在解码的时候不会出现多种可能,就实现了唯一性,前缀编码指的是任意一组编码都不是其他任意组编码的前缀(如a的编码是1,而b的编码是11,这样明显a的编码1是b的编码的前缀)。而我们的哈夫曼编码就是一种前缀编码。

首先我们先要明白对于有n0个叶子节点的哈夫曼树,一共有2 * n0 - 1个节点。因为由二叉树性质可知n0 = n2 + 1,即 n2 = n0 - 1,而二叉树中不存在度为1的节点,即n1 = 0,n = n0 + n1 + n2 = n0 + n2 = n0 + n0 - 1 = 2 * n0 - 1。

创建哈夫曼树

首先我们先要创建哈夫曼树才能构造哈夫曼编码,而创建哈夫曼树的算法的思路是,用数组ht[]来存放哈夫曼树,将先将叶子节点放在数组ht[0,n0]位置(叶子节点的数组的范围为[0,n0) ),然后处理非叶子节点放在ht[n0,2 * n0 - 1] (非叶子节点的数组的范围为[n0,2 * n0 - 1) ):在ht[i]到ht[i - 1]中找到最小的两个节点ht[lnode]和ht[rnode],作为ht[i]的左、右子树,如此循环到n0 - 1个个非叶子节点处理完。

完整的创建哈夫曼树的动图:

构造哈夫曼树的代码如下:

#include <iostream>
using namespace std;typedef struct {char data;          //节点值double weight;     //权重int parent;         //双亲节点int lchild;           //左孩子节点int rchild;          //右孩子节点
}HTNode;//构造哈夫曼树
//ht[]数组来存储哈夫曼树 n0为叶子节点的个数
void creatHT(HTNode ht[], int n0) {double min1, min2;int lnode, rnode;//将所有结点的子父母节点都初始化for (int i = 0; i < 2 * n0 - 1; i++) {ht[i].parent = ht[i].lchild = ht[i].rchild = -1;}//从第n0个开始 因为[0,n0-1]放的是叶子结点而叶子结点要自己另外放入for (int i = n0; i < 2 * n0 - 1; i++) {min1 = min2 = 10000;     // 先初始化一个很大的值以便后面查找最小值lnode = rnode = -1;for (int k = 0; k <= i - 1; k++) {// 只在还未构造的二叉树内查找if (ht[k].parent == -1) {  //查找最小的两个数if (ht[k].weight < min1) {min2 = min1;  rnode = lnode;min1 = ht[k].weight; lnode = k;}else if(ht[k].weight < min2) {min2 = ht[k].weight;rnode = k;}}}//双亲节点的权重等于左孩子节点的权重加上右孩子节点的权重ht[i].weight = ht[lnode].weight + ht[rnode].weight;    //ht[i]为双亲结点ht[i].lchild = lnode;  ht[i].rchild = rnode;// ht[lnode] 和 ht[rnode]的双亲结点的位置是iht[lnode].parent = ht[rnode].parent = i;   }
}int main() {HTNode ht[N];HCode hcd[N];//设A的权重为1 B为2 C为3 D为4for (int i = 0; i < 4; i++) {ht[i].data = 'A' + i;ht[i].weight = 1 + i;}creatHT(ht, 4);return 0;
}

构造哈夫曼编码

构造哈夫曼编码的算法思路为,只需要对叶子节点构造哈夫曼编码,用cd[start,n0]来存放当前节点的哈夫曼编码,在对叶子节点ht[i],找其双亲结点ht[f],如果ht[i]是ht[f]的左孩子则在hcd[i].cd数组中添加0,若是右孩子则添加1,然后将start--。再对ht[f]的双亲结点进行刚才操作,直到其没有双亲结点则退出。

列举一个构造哈夫曼编码的动图:

构造哈夫曼树的代码如下:

void creatHCode(HTNode ht[], HCode hcd[], int n0) {HCode hc;int f, c;//只用给叶子结点生成哈夫曼树for (int i = 0; i < n0; i++) {hc.start = n0;          c = i;f = ht[i].parent;// 循环到无双亲结点为止while (f != -1) {if (ht[f].lchild == c) {         // 当前结点是左孩子的时候hc.cd[hc.start--] = '0';}else{                           // 当前结点是右孩子的时候hc.cd[hc.start--] = '1';}c = f;              //查找下一个  将孩子节点指向双亲结点f = ht[f].parent;  //将双亲结点指向双亲结点的双亲结点}hc.start++;        //start要指向哈夫曼编码的开始值 因为while循环结束还自减了1 所以要把他加回来hcd[i] = hc;  //将临时的hc存储到hcd里}
}

完整的用哈夫曼树构造哈夫曼编码如下:

#include <iostream>
#define N 10
using namespace std;typedef struct {char data;          //节点值double weight;     //权重int parent;         //双亲节点int lchild;           //左孩子节点int rchild;          //右孩子节点
}HTNode;typedef struct {char cd[N];         //用来存储哈夫曼编码int start;           //表示哈夫曼编码的开始下标 则cd[start,n0]范围是哈夫曼编码
}HCode;//构造哈夫曼树
//ht[]数组来存储哈夫曼树 n0为叶子节点的个数
void creatHT(HTNode ht[], int n0) {double min1, min2;int lnode, rnode;//将所有结点的子父母节点都初始化for (int i = 0; i < 2 * n0 - 1; i++) {ht[i].parent = ht[i].lchild = ht[i].rchild = -1;}//从第n0个开始 因为[0,n0-1]放的是叶子结点而叶子结点要自己另外放入for (int i = n0; i < 2 * n0 - 1; i++) {min1 = min2 = 10000;    // 先初始化一个很大的值以便后面查找最小值lnode = rnode = -1;for (int k = 0; k <= i - 1; k++) {if (ht[k].parent == -1) {   // 只在还未构造的二叉树内查找//查找最小的两个数if (ht[k].weight < min1) {min2 = min1;  rnode = lnode;min1 = ht[k].weight; lnode = k;}else if(ht[k].weight < min2) {min2 = ht[k].weight;rnode = k;}}}ht[i].weight = ht[lnode].weight + ht[rnode].weight;     //双亲节点的权重等于左孩子节点的权重加上右孩子节点的权重ht[i].lchild = lnode;   //ht[i]为双亲结点ht[i].rchild = rnode;ht[lnode].parent = ht[rnode].parent = i;   // ht[lnode] 和 ht[rnode]的双亲结点的位置是i}
}void creatHCode(HTNode ht[], HCode hcd[], int n0) {HCode hc;int f, c;//只用给叶子结点生成哈夫曼树for (int i = 0; i < n0; i++) {hc.start = n0;            c = i;f = ht[i].parent;// 循环到无双亲结点为止while (f != -1) {if (ht[f].lchild == c) {         // 当前结点是左孩子的时候hc.cd[hc.start--] = '0';}else{                           // 当前结点是右孩子的时候hc.cd[hc.start--] = '1';}c = f;              //查找下一个  将孩子节点指向双亲结点f = ht[f].parent;  //将双亲结点指向双亲结点的双亲结点}hc.start++;        //start要指向哈夫曼编码的开始值 因为while循环结束还自减了1 所以要把他加回来hcd[i] = hc;  //将临时的hc存储到hcd里}
}int main() {HTNode ht[N];HCode hcd[N];//设A的权重为1 B为2 C为3 D为4for (int i = 0; i < 4; i++) {ht[i].data = 'A' + i;ht[i].weight = 1 + i;}creatHT(ht, 4);creatHCode(ht, hcd, 4);for (int i = 0; i < 4; i++) {//强制类型转换cout << (char)('A' + i) << "的哈夫曼编码为: ";//因为cd[]数组的下标是从start开始递减存储的,而start最开始是等于n0,所以j<=4for (int j = hcd[i].start; j <= 4; j++) {cout << hcd[i].cd[j];}cout << endl;}return 0;
}

运行结果是:

哈夫曼树构造哈夫曼编码相关推荐

  1. 哈夫曼树(带权路径长度+树的带权路径长度+哈夫曼树定义+构造哈夫曼树+哈夫曼树性质+哈夫曼编码+计算平均码长-这里指WPL)

    带权路径长度 树的带权路径长度WPL 哈夫曼树 哈夫曼树构造 哈夫曼树性质 哈夫曼编码 固定长度编码 可变长编码 前缀编码 固定长度编码.可变长编码.前缀编码.哈夫曼编码 思维倒图 试题

  2. 哈夫曼树与哈夫曼编码:

    定义:带权路径长度WPL最小的二叉树(在编写哈夫曼编码时用到的特殊二叉树) 构造过程: 其实就是每次在权值集合中选两个最小结点的组成新树,然后新树的根节点是二者的权值之和,将刚刚两个从集合中删掉,将新 ...

  3. 哈夫曼树及哈夫曼编码详解

    一.预备知识 • 结点的路径长度:从根结点到该结点的路径上所包括的边的数目: • 树的内路径长度:除叶结点外,从根到树中其他所有结点的路径长度之和: • 树的外路径长度:从根结点到树中所有叶子结点的路 ...

  4. 哈夫曼树及哈夫曼编码

    哈夫曼树 哈夫曼树,最优二叉树,带权路径长度(WPL)最短的树.它没有度为1的点,是一棵严格的二叉树(满二叉树). 何谓'带权路径长度' 了解哈夫曼树,我们首先要知道树的几个相关术语,并了解什么是WP ...

  5. 数据结构学习记录——哈夫曼树(什么是哈夫曼树、哈夫曼树的定义、哈夫曼树的构造、哈夫曼树的特点、哈夫曼编码)

    目录 什么是哈夫曼树 哈夫曼树的定义 哈夫曼树的构造 图解操作 代码实现 代码解析 哈夫曼树的特点 哈夫曼编码 不等长编码 二叉树用于编码 哈夫曼编码实例 什么是哈夫曼树 我们先举个例子: 要将百分制 ...

  6. 赫夫曼编码c语言 排序部分,c语言构造哈夫曼树输出哈夫曼编码出错 ,跪求大神帮我找错...

    c语言构造哈夫曼树输出哈夫曼编码出错 ,跪求大神帮我找错0 youxun0952016.09.07浏览120次分享举报 #include #include #include typedef struc ...

  7. 哈夫曼树构造以及代码实现

    哈夫曼树构造以及代码实现 什么是哈夫曼树 理解哈夫曼树 哈夫曼树的构造 哈夫曼树构造-代码实现 什么是哈夫曼树 构造一颗二叉树,该树的带权路径长度达到最小,称为最优二叉树,也称为哈夫曼树(Huffma ...

  8. C语言:哈夫曼树构造及编码(核心代码每一行都有注释)

    一.[实验目的及要求] 理解Huffman树的概念及其存储结构: 熟悉Huffman树的构造: 掌握Huffman树的编码方法. 二.[实验内容] 1.代码实现Huffman编码 2.请统计每个字符出 ...

  9. 算法学习笔记10——应用哈夫曼树构造最短的不等长编码方案

    内容: (1)设需要编码的字符集为{d1, d2, -, dn},它们出现的频率为{w1, w2, -, wn},应用哈夫曼树构造最短的不等长编码方案. 提示: 哈夫曼树(Huffman Tree), ...

最新文章

  1. 音乐与现代计算机技术,计算机技术在音乐教学中应用与研究.doc
  2. 吴恩达深度学习笔记(114)-RNN梯度消失问题详解
  3. vc++6.0获取磁盘基本信息_分享一个实用脚本--一键获取linux内存、cpu、磁盘IO等信息...
  4. 编写windows 控件需要注意的几个标签属性(Attribute)
  5. 盲人可以也做软件工程师,反思一下老哥
  6. Github 完整学习教程
  7. Easyui validatebox修改——1.当text发生变化时在校验,2.取消校验,3扩展自定义验证
  8. 二叉树C++ | 实现删除节点_4
  9. 苹果怎么删除通讯录联系人_苹果手机通讯录怎么恢复?这才是正确的打开方式!...
  10. app登录界面背景 css_Google flutter这么火?撸一个APP登录界面(上)
  11. mysql备份恢复中的常见错误
  12. 重启tomcat-Tomcat服务器怎么重启?
  13. 读取ZIP文件时ZipEntry的size为-1的解决办法
  14. EasyExcel 背景颜色枚举
  15. android 5.1一键root工具箱,s大师一键root下载
  16. idou老师教你学Istio 29:Envoy启动流程
  17. Room使用遇到的问题
  18. NER依存关系模型:原理,建模及代码实现
  19. 211院校实习生三跨Java面经(头条、拼多多、华为、vivo)
  20. 百元级冷门高音质蓝牙耳机推荐,300左右为什么不试试这几款蓝牙耳机?

热门文章

  1. 宝藏又小众的灯饰装修设计素材网站分享
  2. Mac电脑,HBuilderX使用ios模拟器的方法
  3. iOS获取设备型号、设备类型等信息
  4. 洛谷P5082 成绩解题思路及题解
  5. 爬虫第二弹 图片解析
  6. linux内核memset,Linux库memset函数实现
  7. 【BigHereo 11】-----JobReview Team Recall
  8. 给App加上音频编辑功能,让你的用户Show起来
  9. 喜报:安科瑞电能质量在线监测装置取得国家电网检测报告
  10. intrupt 6410裸机中断