C数据结构:哈夫曼树算法实现与应用
带权二叉树
引入路径相关知识:
结点之间的路径长度:到达两结点之间的路径分支数就是路径长度。
树的路径长度:根结点到每一个结点之间的路径长度之和。
解释:因为说的是树,包含整一棵树的,
从根结点到每一孩子结点的路径长度加起来的总和成为 “树” 的路径长度。
对后面解释哈夫曼树非常有用。
权值的解释:
- 例子:好比从广州到成都,两个结点之间所需要的车费就是权值
- 通常给出的是去往下一结点的权值,也就是结点的权值
- A->B,一般会给出B对应的权值,B的权值就是A到B的路径权值
@@我们接下去要特别研究的哈夫曼树其实就是带权的二叉树@@
认识WPL
WPL(带权路径长度)= 节点全值 * 路径长度之和
- 说简单点就是二叉树的带权路径长度之和就是WPL。
(根结点到每一个结点的带权路径之和就是WPL)
最优二叉树
我在构造哈夫曼树的时候意识到如果不按照规定的方法构造二叉树的话就会造成很多种可能,构造出的二叉树也不尽相同,所以我们的前辈哈夫曼博士就想了一个办法就是构造最优二叉树,最优二叉树就是我们要学习的哈夫曼树吗。
- 最优二叉树就是在众多解法中找到WPL最小那一棵二叉树,这一棵就叫做哈夫曼树。
构造哈夫曼树的过程
我对哈夫曼树如何建立的操作的理解:
- 对于给出n个带权结点,这里指的是结点,这些结点暂时还没有连通,每一个结点独立开,把这些结点称为根结点,他们是一个集合。
- 其次我们在这些根结点权值中找到最小的两个结点,然后用一个全新的根结点将其做成二叉树。注意,我们默认把最小的放在左边,次小的(比左边大的)放在右边。
- 该新二叉树的左右两个权值加起来就变成了新的二叉树的根结点权值,把该新根结点放进剩下的结点中作为一个集合。
- 继续回到集合中,找两个最小的权值进行重复操作。
如此一来当根结点只剩下一个的时候就是生成了一个哈夫曼树了。
可以看下图作为一个例子:

总结:哈夫曼树生成过程产生的结点规律:n -> n-1
其实能隐隐约约感受到新生成结点的数量规律,每找到两个权值最小的结点就会生成一个新根结点加进集合中,假设原先有n个根结点在集合中,那规律就是生成一次新根,就会减少一个根结点,那么最后就会生成n-1个新的,注意是新的结点,不是n-1个根结点,我们最后是只剩下一个二叉树根结点。
- 最后二叉树中所有的结点数:n + n - 1 = 2n - 1 ,就是最终的总结点数。
哈夫曼树的应用
最优二叉树优点
- 最优二叉树有一个厉害的点是能够保证每一个叶子结点所经过的路径是不相同的。
- 我们通过这个路径不同的对其进行应用,就可以对应到编码和解码。因为我们能保证每一个在叶子结点的信息都有唯一对应的编码形式。
- 编码容易解码难,因为我们有时候拿的不是自己编的码,需要结别人的编码时候就会显得异常困难。
其实哈夫曼树的精髓就在于构造,不断生成新结点把两小权值连在一起。 这里需要和二叉搜索树进行区分开,因为哈夫曼树针对于叶子结点的,二叉搜索树是每一个结点都有他对应的信息,会用于增删查改。
这里我们就先对哈夫曼树进行深入了解,会了哈夫曼树,二叉搜索树学起来就很简单。
建立哈夫曼树
明确三个任务
- ①先对给出的n根结点的信息复制到一个2n-1的二维数组当中
- ②在2n数组中且在没有父母结点元素中找到最小的那两个元素进行合并,这里的合并并不指真正把他合并,只是把父母结点修改成同一个父母结点就行。
- ③然后就是把两个结点的权值加起来,作为新结点也就是这两个结点的父结点的权值,再把找到的这两个的父母结点修改成同一个。
- 注意,我们找到的父母结点插入位置是在 n + 1 的这个位置开始,然后每找到一个就往下加入,直到插完插到2n-1位置就代表该哈夫曼树建立成功。
- ④在我们寻找最小值的时候那个for循环切记不能越界或者不是当前结点数量。
- ⑤结点数量在变化,我们每次寻找最小结点的时候那个条件判断也要跟着变化,所以这时候结构体中要时刻记录着结点数量,我们可以用结点数量作为循环条件。
注意的细节: - 建立二叉树的时候一定要注意对数组下标不要越界。
- 叶子结点是最终二叉树所有的结点的两倍减一倍数。
- 其次在寻找两个最小结点的时候要找没有父母结点的,也就是还没有组成一对的根结点。
- 组成的新的根结点是加入到n+1的数组下标中,凡是新结点都要接入到数组中。
- 因此寻找最小两个结点的时候也要在新加入的父母结点中找,简单来说就是找最小值的时候一直找没有父母结点的就行,直到剩下最后一个作文二叉树根结点就不需要找了,根结点也没有父母结点。
代码如下:
PS:因为鄙人为了方便自己记录,直接浪费两个数组中第一个元素,都将其设置为空或者不管。因此我们计算的时候就不用绞尽脑汁的去想还要加一的事情了。所以我一般是直接新建2n个空间的数组,然后慢慢存。
结构体代码部分
typedef struct _hafuman{
int wight;
int parent;
int lchild;
int rchild;
}HF;
typedef struct _HF_Tree{
HF hafuman[17];
int hflength;
int leaves;
}HF_Tree;
HF_Tree Tree;
建立操作代码
这里的Find_Minindex(Tree)函数是寻找Tree中最小的一个元素,且返回该元素的下标,找到后要立马把该最小结点的父母结点进行修改,修改后才能继续用,想要找两个就用两次就好了。
void Make_hafuman(HF_Tree* Tree)
{
int i = Tree->leaves+1;
int j, index = 0, lch, rch;
while( i < Tree->leaves*2)
{
lch = Find_Minindex(Tree);
Tree->hafuman[lch].parent = i;
Tree->hafuman[i].lchild = lch;
rch = Find_Minindex(Tree);
Tree->hafuman[rch].parent = i;
Tree->hafuman[i].rchild = rch;
Tree->hafuman[i].wight = (Tree->hafuman[lch].wight + Tree->hafuman[rch].wight);
i++;
Tree->hflength++;
}
}
找到最小结点(※难点)
int Find_Minindex(HF_Tree* Tree)
{
int length = sizeof(Tree->hafuman)/sizeof(HF);
//printf("暂停测试:%d",length);
int i, min = 1000000, index = 0;
for(i = 1; i <= Tree->hflength; i++)
{
if(Tree->hafuman[i].parent == 0)
{
if(Tree->hafuman[i].wight < min)
{
min = Tree->hafuman[i].wight;
index = i;
}
}
}
return index;
}
附上建立哈夫曼树源代码
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct _hafuman{
int wight;
int parent;
int lchild;
int rchild;
}HF;
typedef struct _HF_Tree{
HF hafuman[17];
int hflength;
int leaves;
}HF_Tree;
HF_Tree Tree;
void Make_hafuman(HF_Tree*);
int Find_Minindex(HF_Tree*);
//生成哈夫曼树
int main()
{
/*
给出 n 个元素数组作为根节点权值
生成一个2n 空间数组,先把这个权值赋值进去
结构体数组:权值域,父母域,左孩子,右孩子(左小右大)
生成哈夫曼树函数参数:数组地址,
*/
int w[9] = {0, 7, 19, 2, 6, 32, 3, 21, 10};
int i,sum =0;
for(i = 1; i < sizeof(w)/sizeof(int); i++)
{
Tree.hafuman[i].wight = w[i];
Tree.hafuman[i].lchild = 0;
Tree.hafuman[i].parent = 0;
Tree.hafuman[i].rchild = 0;
Tree.leaves++;
sum+=Tree.hafuman[i].wight;
}
Tree.hflength = Tree.leaves;
printf("%d,总和:%d\n",Tree.leaves, sum);
Make_hafuman(&Tree);
for(i = 1; i <= Tree.hflength; i++)
{
printf("%d:%d,%d,%d,%d\n",i, Tree.hafuman[i].wight, Tree.hafuman[i].parent, Tree.hafuman[i].lchild,Tree.hafuman[i].rchild);
}
return 0;
}
void Make_hafuman(HF_Tree* Tree)
{
int i = Tree->leaves+1;
int j, index = 0, lch, rch;
while( i < Tree->leaves*2)
{
lch = Find_Minindex(Tree);
Tree->hafuman[lch].parent = i;
Tree->hafuman[i].lchild = lch;
rch = Find_Minindex(Tree);
Tree->hafuman[rch].parent = i;
Tree->hafuman[i].rchild = rch;
Tree->hafuman[i].wight = (Tree->hafuman[lch].wight + Tree->hafuman[rch].wight);
i++;
Tree->hflength++;
}
}
int Find_Minindex(HF_Tree* Tree)
{
int length = sizeof(Tree->hafuman)/sizeof(HF);
//printf("暂停测试:%d",length);
int i, min = 1000000, index = 0;
for(i = 1; i <= Tree->hflength; i++)
{
if(Tree->hafuman[i].parent == 0)
{
if(Tree->hafuman[i].wight < min)
{
min = Tree->hafuman[i].wight;
index = i;
}
}
}
return index;
}
收获:让我对二叉搜索树和哈夫曼树有了真正意义上的区分和了解
二叉搜索树是倾向于每一个结点都有其对应信息,是用来存储信息,增删查改。
哈夫曼树是用于解码编码,而且只是研究根结点到叶子结点的权值。
共同点:都需要找到其最优二叉树。一般是左孩子小,右孩子大。
C数据结构:哈夫曼树算法实现与应用的更多相关文章
- 哈夫曼树算法及C++实现
一.相关概念 1.叶子结点的权值(weight)是对叶子结点赋予的一个有意义的数值量. 2.设二叉树有n个带权值的叶子结点,从根节点到各个叶子结点的路径长度与相应叶子结点权值的乘积之和叫做二叉树的带权 ...
- 数据结构-哈夫曼树(python实现)
好,前面我们介绍了一般二叉树.完全二叉树.满二叉树,这篇文章呢,我们要介绍的是哈夫曼树. 哈夫曼树也叫最优二叉树,与哈夫曼树相关的概念还有哈夫曼编码,这两者其实是相同的.哈夫曼编码是哈夫曼在1952年 ...
- C#数据结构-赫夫曼树
什么是赫夫曼树? 赫夫曼树(Huffman Tree)是指给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小.哈夫曼树(也称为最优二叉树)是带权路径长度最短的树,权值较大的结点 ...
- 数据结构-哈夫曼(Huffman)
#include <iostream> #include <cstdio> #include <malloc.h> #define LIST_INIT_SIZE 1 ...
- Java数据结构和算法(四)赫夫曼树
Java数据结构和算法(四)赫夫曼树 数据结构与算法目录(https://www.cnblogs.com/binarylei/p/10115867.html) 赫夫曼树又称为最优二叉树,赫夫曼树的一个 ...
- java 哈夫曼编码
//哈夫曼树类 public class HaffmanTree { //最大权值 ; int nodeNum ; //叶子结点个数 public HaffmanTree(int n) { this. ...
- C++ 漫谈哈夫曼树
1. 前言 什么是哈夫曼树? 把权值不同的n个结点构造成一棵二叉树,如果此树满足以下几个条件: 此 n 个结点为二叉树的叶结点 . 权值较大的结点离根结点较近,权值较小的结点离根结点较远. 该树的带权 ...
- 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- SDUT 3345 数据结构实验之二叉树六:哈夫曼编码
数据结构实验之二叉树六:哈夫曼编码 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 字符的编 ...
- 【数据结构】赫夫曼树的实现和模拟压缩(C++)
赫夫曼(Huffman)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同 ...
随机推荐
- 7 CSS选择器优先级
7 选择器优先级 所谓CSS优先级,即是指CSS样式在浏览器中被解析的先后顺序.样式表中的特殊性描述了不同规则的相对权重. /* !important > 行内样式>ID选择器 > ...
- Codeforces Round #751 (Div. 1)
CF1601A Array Elimination 洛谷传送门 CF1601A 分析 可以发现每一位可以拆开,也就是每一位的一的个数一定是 \(k\) 的倍数, 直接求 \(\gcd\) 出来,它的约 ...
- #期望dp#51nod 2015 诺德街
题目传送门 分析 禁不住 QuantAsk 的诱惑(bushi) 考虑一条路线可以由若干段 \(1-2-\dots-n-\dots-2\) 以及 最后一段 \(1-\dots-x\) 组成. 对于最后 ...
- 震撼!这个Python模块竟然能自动修复代码!
说到Python的强大的地方,那真的是太多了,优雅.简洁.丰富且强大的第三方库.开发速度快,社区活跃度高等,所以才使得Python才会如此的受欢迎. 今天给大家介绍一个特别暴力的Python库: Fu ...
- 【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(2)
1.问题描述: 开发服务端推送,客户端能收到离线推送,但是推送收到的通知只能从手机顶部下拉看到,无法收到一个顶部的弹框.请问是什么原因? 解决方案: 可能原因一: 消息提醒的方式与消息类别有关,比如: ...
- 电脑开机时报错No Bootable Device找不到索引的解决方法
本文介绍笔记本电脑出现No Bootable Device错误提示,且无法开机的多种解决办法. 1 问题产生 最近,笔记本电脑正在正常使用时,突然蓝屏,出现你的设备遇到问题,需要重启.的提示: ...
- maven 创建spring boot 需要的配置[一]
前言 之所以写这个是因为现在官方推荐云创建: 所以标注一下maven project,创建后,如何导入spring boot. 正文 1.步骤一 在pom.xml 中加入: <dependenc ...
- Nginx 简介、安装与配置文件详解
〇.前言 在日常工作中,Nginx 的重要性当然不言而喻. 经常用,但并不意味着精通,还会有很多不清楚的方式和技巧,那么本文就简单汇总下,帮助自己理解. 一.Nginx 简介 1.1 关于 Nginx ...
- Python阿里云消息推送调用API
很多公司测试APP推送时候,应该也是很头疼:推送环境:测试.正式,稍不注意就把测试的push到正式上,导致所有用户都收到 例子很多: 其实阿里.极光都有推送Api,直接调用API就ok,特别是有的公司 ...
- Crazy Excel:Excel中的泥石流
Crazy Excel又名:疯狂Excel.是一款PC端的Excel软件工具,该软件支持windows, mac os等主流操作系统. 正如其名,作者在设计之初就加入了一些疯狂的设计,目的是创作出更加 ...