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)树,由发明它的人物命名,又称最优树,是一类带权路径最短的二叉树,主要用于数据压缩传输. 赫夫曼树的构造过程相对比较简单,要理解赫夫曼数,要先了解赫夫曼编码. 对一组出现频率不同 ...
随机推荐
- #贪心#CF1054D Changing Array
题目 给定 \(n\) 个 \(k\) 位二进制数,\(n\leq 2*10^5,k\leq 30\) 可以选择若干数将其所有二进制位取反, 最多可以有多少个区间的异或和不为 0 分析 考虑将区间异或 ...
- Java中IO和NIO的本质和区别
目录 简介 IO的本质 DMA和虚拟地址空间 IO的分类 IO和NIO的区别 总结 简介 终于要写到java中最最让人激动的部分了IO和NIO.IO的全称是input output,是java程序跟外 ...
- Git 安全远程访问:SSH 密钥对生成、添加和连接步骤解析
使用 SSH 密钥对的 Git 安全远程访问:生成.添加和连接 SSH(Secure Shell)是一种用于安全远程访问的协议,它提供了加密通信和身份验证机制.在使用 SSH 连接到远程 Git 存储 ...
- C# Winform Socket点对点通信
前言 Socket的英文原义是"孔"或"插座",其实在网络编程中Socket就是这个意思,就像我们打电话,要首先知道对方的手机号一样,这个手机号就相当于一个So ...
- HarmonyOS极客松“上分秘籍”! 高手们顶峰相见!
HarmonyOS 极客马拉松2023 火热进行中,我们期待与各位开发者相聚一起,践行极客精神,创造无限可能! 我们鼓励各位极客们自由组队,挥洒创意,用HarmonyOS 探索移动应用和服务的更多 ...
- 安全工具分析系列-Londly01
前言 原创作者:Super403,文章分析主要用于研究教学 本期研究:[Londly01-safety-tool]工具源码 简介:自动化资产探测及漏扫脚本 工具来源:https://github.co ...
- redis 简单整理——redis 的列表基本结构和命令[四]
前言 简单整理一下redis的列表. 正文 列表(list)类型是用来存储多个有序的字符串,如图2-18所示,a. b.c.d.e五个元素从左到右组成了一个有序的列表,列表中的每个字符串 称为元素(e ...
- Java实现学生投票系统
"感谢您阅读本篇博客!如果您觉得本文对您有所帮助或启发,请不吝点赞和分享给更多的朋友.您的支持是我持续创作的动力,也欢迎留言交流,让我们一起探讨技术,共同成长!谢谢!" 代码 im ...
- 【hibernate】使用HQL对页面进行时间校验操作(预约)
[hibernate]使用HQL对页面进行时间校验操作(预约) 预约系统中的时间校验 正好接了一个预约的需求,还需要用java 7和hibernate 1.时间冲突,时间段不能重复,在保存前对数据库进 ...
- 5款开源、美观、强大的WPF UI组件库
前言 经常看到有小伙伴在DotNetGuide技术社区交流群里提问:WPF有什么好用或者好看的UI组件库?,今天大姚给大家分享5款开源.美观.强大.简单易用的WPF UI组件库. WPF介绍 WPF ...