(数据结构)哈夫曼树
哈夫曼树相关的几个名词
图1 哈夫曼树
路径:在一棵树中,一个结点到另一个结点之间的通路,称为路径
图 1 中,从根结点到结点 a 之间的通路就是一条路径
路径长度:在一条路径中,每经过一个结点,路径长度都要加 1
图 1 中,从根结点到结点 c 的路径长度为
结点的权:给每一个结点赋予一个新的数值,被称为这个结点的权
图 1 中,结点 a 的权为 ,结点 b 的权为
结点的带权路径长度:指的是从根结点到该结点之间的路径长度与该结点的权的乘积
图 1 中,结点 b 的带权路径长度为
WPL:树的带权路径长度为树中所有叶子结点的带权路径长度之和
图 1 中,所示的这颗树的带权路径长度为:
什么是哈夫曼树
当用 n 个叶子结点构建一棵树时,如果构建的这棵树的带权路径长度(WPL)最小,称这棵树为最优二叉树,有时也叫赫夫曼树或者哈夫曼树
在构建哈弗曼树时,要使树的带权路径长度最小,只需要遵循一个原则:权重越大的结点离树根越近
构建哈夫曼树的过程
对于给定的有各自权值的 n 个叶子结点,构建哈夫曼树有一个行之有效的办法:
- 在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和
- 在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推
- 重复 1 和 2 ,直到所有的结点构建成了一棵二叉树为止,这棵树就是哈夫曼树
图 2 哈夫曼树的构建过程
上图 2中,给定了4个叶子结点,如果我们想构建哈夫曼树,构建过程是怎么样的呢!
第一步:在4个权值中选出二个最小的权值(2 和 4),将这二个权值对应的结点组成一个新的二叉树,且新二叉树的根结点(第五个结点)的权值为结点 c 和 d 的权值和 ,在原有4个权值中删除权值为 2 和 4后得到 7 和 5 二个权值,将新的权值 6 加入其中
第二步:在3个权值中选出二个最小的权值(5 和 6),将这二个权值对应的结点组成一个新的二叉树,且新二叉树的根结点(第六个结点)的权值为结点 b 和第五个结点的权值和 ,在原有3个权值中删除权值为 5 和 6后得到 7 一个权值,将新的权值 11 加入其中
第三步:在2个权值中选出二个最小的权值(7 和 11),将这二个权值对应的结点组成一个新的二叉树,且新二叉树的根结点(第七个结点)的权值为结点 a 和第六个结点的权值和 ,在原有2个权值中删除权值为 7 和 11,将新的权值 18 加入其中
第四步:此时已经构成了二叉树!这棵树称为哈夫曼树
哈弗曼树中结点结构
构建哈夫曼树时,首先需要确定树中结点的构成(叶子结点和构建结点)
由于哈夫曼树的构建是从叶子结点开始,不断地构建新的父结点,直至树根,所以结点中应包含指向父结点的指针,但是在使用哈夫曼树时是从树根开始,根据需求遍历树中的结点,因此每个结点需要有指向其左孩子和右孩子的指针
// 哈夫曼树结点结构
typedef struct{int weight; // 结点权重int parent, left, right;// 父结点、左孩子、右孩子在数组中的位置下标
} HTNode;
对实现哈夫曼树的代码实现
完全代码!!!
#include <stdio.h>
#include <stdlib.h>// 哈夫曼树结点结构
typedef struct{int weight; // 结点权重int parent, left, right;// 父结点、左孩子、右孩子在数组中的位置下标
} HTNode;void Select(HTNode *HT, int end, int *s1, int *s2){int min1, min2;// 遍历数组初始下标为 1int i = 1;// 找到还没构建树的结点while(HT[i].parent != 0 && i <= end){i++;} // i=1 ——第一个树叶结点 min1 = HT[i].weight; // 7 5 2 4*s1 = i; // 初始化权重最小位置为 1 i++;while(HT[i].parent != 0 && i <= end){i++;}// 对找到的两个结点比较大小(min2为大的,min1为小的) if(HT[i].weight < min1){min2 = min1;*s2 = *s1;min1 = HT[i].weight;*s1 = i;}else{min2 = HT[i].weight;*s2 = i;}// 两个结点和后续的所有未构建成树的叶子结点做比较for(int j = i+1; j <= end; j++){ // j=3(直到 j = end) // 如果有父结点,直接跳过,进行下一个if(HT[j].parent != 0){continue;}// 如果比最小的还小,将 min2=min1,min1赋值新的结点的下标if(HT[j].weight < min1){min2 = min1;min1 = HT[j].weight;*s2 = *s1;*s1 = j;}else if(HT[j].weight >= min1 && HT[j].weight < min2){// 如果介于两者之间,min2赋值为新的结点的位置下标min2 = HT[j].weight;*s2 = j;}}
}HTNode *CreateHuffmanTree(HTNode *HT, int *w, int n){if(n <= 1) return HT;int m = 2*n-1; // 哈夫曼树总节点数,n是叶子结点(m=7) HT = (HTNode*) malloc((m+1)*sizeof(HTNode)); // HT[7]HTNode *p = HT;// 初始化哈夫曼树中的所有叶子结点for(int i = 1; i <= n; i++){ // n=4p[i].weight = w[i-1]; // p[1].weight = w[0] ... p[4].weight = w[3]p[i].parent = 0;p[i].left = 0;p[i].right = 0;}// 初始化除叶子结点外的其它结点 for(int i = n+1; i <= m; i++){p[i].weight = 0;p[i].parent = 0;p[i].left = 0;p[i].right = 0;}// 构建哈夫曼树for(int i = n+1; i <= m; i++){int s1, s2; // 保存了 HT数组中 weight较小值的二个下标 Select(HT, i-1, &s1, &s2); // s1 = 3,s2 = 4// s1 = 1,s2 = 5// s1 = 1,s2 = 6HT[s1].parent = HT[s2].parent = i; // 权重为 2和 4的二个结点的双亲为第五个结点 // 权重为 5和 6的二个结点的双亲为第六个结点// 权重为 7和 11的二个结点的双亲为第七个结点HT[i].left = s1; // 初始化第五个结点的左孩子为权重为 2 的结点// 初始化第六个结点的左孩子为权重为 5 的结点// 初始化第七个结点的左孩子为权重为 7 的结点HT[i].right = s2; // 初始化第五个结点的右孩子为权重为 4 的结点 // 初始化第六个结点的右孩子为权重为 6 的结点 // 初始化第七个结点的右孩子为权重为 11 的结点HT[i].weight = HT[s1].weight + HT[s2].weight; // 初始化第五个结点的权重为 2+4 = 6 // 初始化第六个结点的权重为 5+6 = 11// 初始化第七个结点的权重为 7+11 = 18}return HT;
}int main(void){HTNode *ht = NULL;int weight[4] = {7, 5, 2, 4};ht = CreateHuffmanTree(ht, weight, 4);printf("树根结点权重:%d\n", ht[7].weight); // 树根结点权重:18 return 0;
}
部分函数代码体!!!
// 两个结点和后续的所有未构建成树的叶子结点做比较
for(int j = i+1; j <= end; j++){ // j=3(直到 j = end) // 如果有父结点,直接跳过,进行下一个if(HT[j].parent != 0){continue;}// 如果比最小的还小,将 min2=min1,min1赋值新的结点的下标if(HT[j].weight < min1){min2 = min1;min1 = HT[j].weight;*s2 = *s1;*s1 = j;}else if(HT[j].weight >= min1 && HT[j].weight < min2){// 如果介于两者之间,min2赋值为新的结点的位置下标min2 = HT[j].weight;*s2 = j;}
}
如上函数(Select)中查找权重值最小的两个结点的思想是:从数组起始位置开始,首先找到两个无父结点的结点(说明还未使用其构建成树),然后和后续无父结点的结点依次做比较,有两种情况需要考虑:
- 如果比两个结点中较小的那个还小,就保留这个结点,删除原来较大的结点
- 如果介于两个结点权重值之间,替换原来较大的结点
(数据结构)哈夫曼树相关推荐
- 数据结构---哈夫曼树
数据结构-哈夫曼树 原理:参考趣学数据结构 代码: #include<stdio.h> #include<stdlib.h> #define N 100 #define INF ...
- 【数据结构——哈夫曼树及其应用】
[数据结构--哈夫曼树及其应用] 一.哈夫曼树的基本概念 二.哈夫曼树的构造算法 (一)哈夫曼树的构造过程 (二)哈夫曼树构造算法的实现 1.初始化 2.创建树 3.完整的创建哈夫曼树代码 三.哈夫曼 ...
- 数据结构哈夫曼树实现26个英文字符的编码和译码
数据结构哈夫曼树实现26英文字符的编码和译码 那么首先什么是哈夫曼树?(知道的略过,直奔下面代码就好!) 在计算机数据处理中,霍夫曼编码使用变长编码表对源符号(如文件中的一个字母)进行编码,其中变长编 ...
- 数据结构--赫夫曼树
数据结构 –赫夫曼树 文章目录 数据结构 一.一些概念 二.最优二叉树(赫夫曼树) 三.赫夫曼树的构造 四.赫夫曼编码 五.前缀编码 一.一些概念 路径:从树中一个结点到另一个结点之间的分支构成这两个 ...
- 数据结构(哈夫曼树,哈夫曼编码)入门篇,JAVA实现
什么是哈夫曼树 哈夫曼树就是一种最优判定树,举个例子,如下一个判断逻辑 if(s<60) g=1; else if(s<70) g=2 else if(s<80) g=3 else ...
- 数据结构 - 赫夫曼树
wpl最小的就是赫夫曼树(所有叶子节点的带权路径长度之和最小) 写出来两个节点连接,然后循环就可以了 package tree.huffmantree;import java.util.ArrayLi ...
- 数据结构--赫夫曼树及其应用
讲解请参考 赫夫曼 ------ 赫夫曼树和赫夫曼编码的存储表示------ typedef struct {unsigned int weight;unsigned int parent,lchil ...
- 数据结构哈夫曼树(C语言版)
文章目录 一. 问题 需求分析 代码分析 结构体定义使用 建立哈夫曼树,首先需要找到两个权值最小的两个叶子结点,然后建树 哈夫曼编码(我采用的是从叶子结点-->根节点,所以实际是反过来的) 使用 ...
- 数据结构——哈夫曼树
1.介绍 哈夫曼树就是树的带权路径长度(即WPL)最小的树,WPL等于所有叶节点的带权路径长度之和. 而叶节点的带权路径长度=该结点的路径长度*该结点的权值. 结点的路径长度就是从根节点到该结点所经历 ...
- 算法与数据结构 --- 哈夫曼树及其应用
第一部分 --- 哈夫曼树的基本概念 对一个判断树的判断次序进行改变后判断的总次数就可能截然不同 如上图,在面对一万个数据的时候,左边的判断树的判断总次数为22000次,右边的判断树的判断总次数为31 ...
最新文章
- 如何leangoo看板工具做可视化工作流
- 走出回归测试困境,爱奇艺精准测试体系建设
- 7系列FPGA逻辑单元理解
- 抽象工厂类--一个简单的例子
- altium designer 单层显示切换两次_新论文:北京新机场装配式单层铝合金网壳结构整体稳定性能研究...
- 一个好用的时间管理Chrome扩展 - Calendar and Countdown
- 如何写登录的记住账号
- vs 2010 不显示解决方案文件
- win10个人壁纸默认保存位置
- 什么原数据更容易平稳_为什么老年人更容易患上艾滋病?
- C++11/14::右值引用
- (转)高新技术在高频交易中的运用
- 《软件工程导论》第一章
- Adams2019安装教程链接分享
- Smart 200PLC PC Access SMART OPC通信
- 最长递增子序列的O(NlogN)算法
- Google之Stopwatch 计时器
- 【OneDrive篇】OneDrive禁用个人保管库(网页端)
- Unnecessary escape character: \- no-useless-escape eslint
- java知识点博客园_Java知识点总结1