哈夫曼树算法及C++实现
一、相关概念
1、叶子结点的权值(weight)是对叶子结点赋予的一个有意义的数值量。
2、设二叉树有n个带权值的叶子结点,从根节点到各个叶子结点的路径长度与相应叶子结点权值的乘积之和叫做二叉树的带权路径长度。
3、给定一组具有确定权值的叶子结点,可以构造出不同的二叉树,将其中带权路径长度最小的二叉树称为哈夫曼树。
二、哈夫曼算法基本思想
(1) 以权值分别为W1,W2...Wn的n各结点,构成n棵二叉树T1,T2,...Tn并组成森林F={T1,T2,...Tn},其中每棵二叉树 Ti仅有一个权值为 Wi的根结点;
(2) 在F中选取两棵根结点权值最小的树作为左右子树构造一棵新二叉树,并且置新二叉树根结点权值为左右子树上根结点的权值之和(根结点的权值=左右孩子权值之和,叶结点的权值= Wi);
(3) 从F中删除这两棵二叉树,同时将新二叉树加入到F中;
(4) 重复(2)、(3)直到F中只含一棵二叉树为止,这棵二叉树就是Huffman树。
三、哈夫曼算法的存储结构
考虑到对于有n个叶子结点的哈夫曼树有2n-1个结点,并且进行n-1次合并操作,为了便于选取根节点权值最小的二叉树以及合并操作,设置一个数组haftree[2n-1],保存哈夫曼树中的各个结点的信息,数组元素的结点结构如下图所示:
| weight | lchild | rchild | parent |
哈夫曼树的结点结构
其中,weight保存结点权值;
lchild保存该节点的左孩子在数组中的下标;
rchild保存该节点的右孩子在数组中的下标;
parent保存该节点的双亲孩子在数组中的下标。
可以用C++语言中的结构体类型定义上述结点,如下:
// 哈夫曼树的结点结构
struct element
{
int weight; // 权值域
int lchild, rchild, parent; // 该结点的左、右、双亲结点在数组中的下标
};
四、哈夫曼算法C++实现
为了判定一个结点是否已经加入哈夫曼树中,可通过parent域的值来确定。初始时parent的值为-1,当某结点加入到树中时,该节点parent域的值为其双亲结点在数组中的下标。
构造哈夫曼树时,首先将n个权值的叶子结点存放到数组haftree的前n个分量中,然后不断将两棵子树合并为一棵子树,并将新子树的根节点顺序存放到数组haftree的前n个分量的后面。
哈夫曼算法用伪代码描述为:
1、数组haftree初始化,所有数组元素的双亲、左右孩子都置为-1;
2、数组haftree的前n个元素的权值置给定权值;
3、进行n-1次合并
3.1 在二叉树集合中选取两个权值最小的根节点,其下标分别为i1,i2;
3.2 将二叉树i1、i2合并为一棵新的二叉树k。
哈夫曼算法完整代码描述如下;
#include<iostream>
#include <iomanip>//这个头文件是声明一些 “流操作符”的
//比较常用的有:setw(int);//设置显示宽度,left//right//设置左右对齐。 setprecision(int);//设置浮点数的精确度。
using namespace std;
// 哈夫曼树的结点结构
struct element
{
int weight; // 权值域
int lchild, rchild, parent; // 该结点的左、右、双亲结点在数组中的下标
};
// 选取权值最小的两个结点
void selectMin(element a[],int n, int &s1, int &s2)
{
for (int i = ; i < n; i++)
{
if (a[i].parent == -)// 初始化s1,s1的双亲为-1
{
s1 = i;
break;
}
}
for (int i = ; i < n; i++)// s1为权值最小的下标
{
if (a[i].parent == - && a[s1].weight > a[i].weight)
s1 = i;
}
for (int j = ; j < n; j++)
{
if (a[j].parent == -&&j!=s1)// 初始化s2,s2的双亲为-1
{
s2 = j;
break;
}
}
for (int j = ; j < n; j++)// s2为另一个权值最小的结点
{
if (a[j].parent == - && a[s2].weight > a[j].weight&&j != s1)
s2 = j;
}
}
// 哈夫曼算法
// n个叶子结点的权值保存在数组w中
void HuffmanTree(element huftree[], int w[], int n)
{
for (int i = ; i < *n-; i++) // 初始化,所有结点均没有双亲和孩子
{
huftree[i].parent = -;
huftree[i].lchild = -;
huftree[i].rchild = -;
}
for (int i = ; i < n; i++) // 构造只有根节点的n棵二叉树
{
huftree[i].weight = w[i];
}
for (int k = n; k < * n - ; k++) // n-1次合并
{
int i1, i2;
selectMin(huftree, k, i1, i2); // 查找权值最小的俩个根节点,下标为i1,i2
// 将i1,i2合并,且i1和i2的双亲为k
huftree[i1].parent = k;
huftree[i2].parent = k;
huftree[k].lchild = i1;
huftree[k].rchild = i2;
huftree[k].weight = huftree[i1].weight + huftree[i2].weight;
} }
// 打印哈夫曼树
void print(element hT[],int n)
{
cout << "index weight parent lChild rChild" << endl;
cout << left; // 左对齐输出
for (int i = ; i < n; ++i)
{
cout << setw() << i << " ";
cout << setw() << hT[i].weight << " ";
cout << setw() << hT[i].parent << " ";
cout << setw() << hT[i].lchild << " ";
cout << setw() << hT[i].rchild << endl;
}
}
int main()
{
int x[] = { ,,,,,,, }; // 权值集合
element *hufftree=new element[*-]; // 动态创建数组
HuffmanTree(hufftree, x, );
print(hufftree,);
system("pause");
return ;
}
最后的输出结果为:

参考文献:
[1]王红梅, 胡明, 王涛. 数据结构(C++版)[M]. 北京:清华大学出版社。
2018-01-03
哈夫曼树算法及C++实现的更多相关文章
- java 哈夫曼编码
//哈夫曼树类 public class HaffmanTree { //最大权值 ; int nodeNum ; //叶子结点个数 public HaffmanTree(int n) { this. ...
- C++ 漫谈哈夫曼树
1. 前言 什么是哈夫曼树? 把权值不同的n个结点构造成一棵二叉树,如果此树满足以下几个条件: 此 n 个结点为二叉树的叶结点 . 权值较大的结点离根结点较近,权值较小的结点离根结点较远. 该树的带权 ...
- 哈夫曼(huffman)树和哈夫曼编码
哈夫曼树 哈夫曼树也叫最优二叉树(哈夫曼树) 问题:什么是哈夫曼树? 例:将学生的百分制成绩转换为五分制成绩:≥90 分: A,80-89分: B,70-79分: C,60-69分: D,<60 ...
- (哈夫曼树)HuffmanTree的java实现
参考自:http://blog.csdn.net/jdhanhua/article/details/6621026 哈夫曼树 哈夫曼树(霍夫曼树)又称为最优树. 1.路径和路径长度在一棵树中,从一个结 ...
- 数据结构之C语言实现哈夫曼树
1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,……,kj, 使得 ki是ki+1 的双亲(1<=i<j),则称此结点序列是从 k1 到 kj 的路径. 从 ...
- (转载)哈夫曼编码(Huffman)
转载自:click here 1.哈夫曼编码的起源: 哈夫曼编码是 1952 年由 David A. Huffman 提出的一种无损数据压缩的编码算法.哈夫曼编码先统计出每种字母在字符串里出现的频率, ...
- C++哈夫曼树编码和译码的实现
一.背景介绍: 给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree).哈夫曼树是带权路径长度最短的树,权值较大的 ...
- 数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- HDU2527 哈夫曼编码
Safe Or Unsafe Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
随机推荐
- DataAdapter对象
DataAdapter对象在物理数据库表和内存数据表(结果集)之间起着桥梁的作用.它通常与DataTable对象或DataSet对象配合来实现对数据库的操作. DataAdapter对象是一个双向通道 ...
- MySQL表级约束和列级约束
对一个数据列建立的约束,称为列级约束 对多个数据列建立的约束,称为表级约束 列级约束即可以在列定义时生命,也可以在列定义后声明. 表级约束只能在列定义后声明. NOT NULL和DEFAULT只存在列 ...
- redis和memcache缓存击穿,缓存失效问题
我们在用缓存的时候,不管是Redis或者Memcached,基本上会通用遇到以下三个问题: 缓存穿透 缓存并发 缓存失效 一.缓存穿透 Paste_Image.png Paste_Image.png ...
- mysql 导入 excel 数据
客户准备了一些数据存放在 excel 中, 让我们导入到 mysql 中.先上来我自己把数据拷贝到了 txt 文件中, 自己解析 txt 文件,用 JDBC 循环插入到数据库中. 后来发现有更简单 ...
- unity制作人物残影-绘制的方法
这里是利用skinnedMeshRenderer原理做的 所以脚本需要挂在带这个组件的模型上 模型shader 必须要有个_Color参数属性,并且这个值可以调节颜色,会改变人物整体的透明度 [代码下 ...
- Python生成pyc文件
Python生成pyc文件 pyc文件是py文件编译后生成的字节码文件(byte code).pyc文件经过python解释器最终会生成机器码运行.所以pyc文件是可以跨平台部署的,类似Java的.c ...
- 机器学习——LightGBM
基础概念 LigthGBM是boosting集合模型中的新进成员,它和xgboost一样是对GBDT的高效实现,很多方面会比xgboost表现的更为优秀.原理上它和GBDT及xgboot类似,都采用损 ...
- SpringMVC的数据回现
一.什么是数据回显 数据提交后,如果出现错误,将刚才提交的数据回显到刚才的提交页面. 二.pojo数据回显方法 1.springmvc默认对pojo数据进行回显. pojo数据传入controller ...
- UILable 标题加粗代码
UILable 标题加粗代码: 加粗; [UILabel setFont:[UIFont fontWithName:@"Helvetica-Bold" size:18]]; 加粗并 ...
- 深入理解JavaScript系列(44):设计模式之桥接模式
介绍 桥接模式(Bridge)将抽象部分与它的实现部分分离,使它们都可以独立地变化. 正文 桥接模式最常用在事件监控上,先看一段代码: addEvent(element, 'click', getBe ...