0. 数据结构图文解析系列

数据结构系列文章
数据结构图文解析之:数组、单链表、双链表介绍及C++模板实现
数据结构图文解析之:栈的简介及C++模板实现
数据结构图文解析之:队列详解与C++模板实现
数据结构图文解析之:树的简介及二叉排序树C++模板实现.
数据结构图文解析之:AVL树详解及C++模板实现
数据结构图文解析之:二叉堆详解及C++模板实现
数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现
数据结构图文解析之:直接插入排序及其优化(二分插入排序)解析及C++实现

1. 哈夫曼编码简介

哈夫曼编码(Huffman Coding)是一种编码方式,也称为“赫夫曼编码”,是David A. Huffman1952年发明的一种构建极小多余编码的方法。
在计算机数据处理中,霍夫曼编码使用变长编码表对源符号进行编码,出现频率较高的源符号采用较短的编码,出现频率较低的符号采用较长的编码,使编码之后的字符串字符串的平均长度 、期望值降低,以达到无损压缩数据的目的。
举个例子,现在我们有一字符串:

this is an example of a huffman tree

这串字符串有36个字符,如果按普通方式存储这串字符串,每个字符占据1个字节,则共需要36 * 1 * 8 = 288bit。
经过分析我们发现,这串字符串中各字母出现的频率不同,如果我们能够按如下编码:

字母 频率 编码 --- 字母 频率 编码
space 7 111 s 2 1011
a 4 010 t 2 0110
e 4 000 l 1 11001
f 3 1101 o 1 00110
h 2 1010 p 1 10011
i 2 1000 r 1 11000
m 2 0111 u 1 00111
n 2 0010 x 1 10010

编码这串字符串,只需要:
(7+4+4)x3 + (3+2+2+2+2+2+2)x4 + (1+1+1+1+1+1)x 5 = 45+60+30 = 135bit
编码这串字符串只需要135bit!单单这串字符串,就压缩了288-135 = 153bit。

那么,我们如何获取每个字符串的编码呢?这就需要哈夫曼树了。
源字符编码的长短取决于其出现的频率,我们把源字符出现的频率定义为该字符的权值。

2. 哈夫曼树简介

哈夫曼又称最优二叉树。是一种带权路径长度最短的二叉树。它的定义如下。

哈夫曼树的定义

假设有n个权值{w1,w2,w3,w4...,wn},构造一棵有n个节点的二叉树,若树的带权路径最小,则这颗树称作哈夫曼树。这里面涉及到几个概念,我们由一棵哈夫曼树来解释

  • 路径与路径长度:从树中一个节点到另一个节点之间的分支构成了两个节点之间的路径,路径上的分支数目称作路径长度。若规定根节点位于第一层,则根节点到第H层的节点的路径长度为H-1.如树b:100到60 的路径长度为1;100到30的路径长度为2;100到20的路径长度为3。

  • 树的路径长度:从根节点到每一节点的路径长度之和。树a的路径长度为1+1+2+2+2+2 = 10;树b的路径长度为1+1+2+2+3+3 = 12.
  • 节点的权:将树中的节点赋予一个某种含义的数值作为该节点的权值,该值称为节点的权;
  • 带权路径长度:从根节点到某个节点之间的路径长度与该节点的权的乘积。例如树b中,节点10的路径长度为3,它的带权路径长度为10 * 3 = 30;
  • 树的带权路径长度:树的带权路径长度为所有叶子节点的带权路径长度之和,称为WPL。树a的WPL = 2*(10+20+30+40) = 200 ;树b的WPL = 1x40+2x30+3x10+3x20 = 190.而哈夫曼树就是树的带权路径最小的二叉树

3. 构造哈夫曼树

3.1 哈夫曼树的节点结构

/*哈夫曼树的节点定义*/
template <typename T>
struct HuffmanNode
{HuffmanNode(T k,HuffmanNode<T>*l=nullptr,HuffmanNode<T>* r=nullptr):key(k),lchild(l), rchild(r){}~HuffmanNode(){};T key;                                     //节点的权值HuffmanNode<T>* lchild;        //节点左孩HuffmanNode<T>* rchild;        //节点右孩
};
  1. value: 节点的权值
  2. lchild:节点左孩子
  3. rchild:节点右孩子

3.2 哈夫曼树的抽象数据类型

template <typename T>
class Huffman
{
public:void preOrder();              //前序遍历哈夫曼树void inOrder();                  //中序遍历哈夫曼树void postOrder();              //后序遍历哈夫曼树void creat(T a[], int size);  //创建哈夫曼树void destory();                  //销毁哈夫曼树void print();                  //打印哈夫曼树Huffman();~Huffman(){};private:void preOrder(HuffmanNode<T>* pnode);void inOrder(HuffmanNode<T>* pnode);void postOrder(HuffmanNode<T>*pnode);void print(HuffmanNode<T>*pnode);void destroy(HuffmanNode<T>*pnode);private:HuffmanNode<T>* root;     //哈夫曼树根节点deque<HuffmanNode<T>*> forest;//森林
};
  1. root:哈夫曼树的根结点。
  2. forset : 森林,这里使用deque来存储森林中树的根节点。

3.3 哈夫曼树的构造步骤

假设有n个权值,则构造出的哈夫曼树有n个叶子节点.n个权值记为{w1,w2,w3...wn},哈夫曼树的构造过程为:

  1. 将w1,w2,w3...wn看成具有n棵树的森林,每棵树仅有一个节点。
  2. 从森林中,选取两棵根节点权值最小的树,两棵树分别作为左子树与右子树,构建一棵新树。新树的权值等于左右子树权值之和。
  3. 从森林中删除两棵权值最小的树,将构建完成后的新树加入森林中。
  4. 重复2、3步骤,直到森林只剩一棵树为止。这棵树便是哈夫曼树。

图一的树b为一棵哈夫曼树,它的叶子节点为{10,20,30,40},以这4个权值构建树b的过程为:

这个过程很编码实现为:

/*创建哈夫曼树*/
template<typename T>
void Huffman<T>::creat(T a[],int size)
{for (int i = 0; i < size; i++) //每个节点都作为一个森林{//为初始序列的元素构建节点。每个节点作为一棵树加入森林中。HuffmanNode<T>* ptr = new HuffmanNode<T>(a[i],nullptr,nullptr);  forest.push_back(ptr);}for (int i = 0; i < size - 1; i++){//排序,以选出根节点权值最小两棵树sort(forest.begin(), forest.end(), [](HuffmanNode<T>* a, HuffmanNode<T>*b){return a->key< b->key; });    HuffmanNode<T>*node = new HuffmanNode<T>(forest[0]->key + forest[1]->key, forest[0], forest[1]); //构建新节点forest.push_back(node);  //新节点加入森林中forest.pop_front(); //删除两棵权值最小的树forest.pop_front();}root = forest.front();forest.clear();
};
  1. 这里仅仅示范构建哈夫曼树的过程,没有经过性能上的优化和完善的异常处理。这里使用deque双端队列来存储森林中树根节点,使用库函数sort来对根节点依权值排序。这里也可以使用我们之前介绍的小顶堆来完成工作。

3.4 哈夫曼树的其他操作

其他操作在前几篇博文中都有介绍过,这里就不再啰嗦,可以在文章底部链接取得完整的工程源码。
这里贴出测试时需要的代码:

/*打印哈夫曼树*/
template<typename T>
void Huffman<T>::print(HuffmanNode<T>* pnode)
{if (pnode != nullptr){cout << "当前结点:" << pnode->key<<".";if (pnode->lchild != nullptr)cout << "它的左孩子节点为:" << pnode->lchild->key << ".";else cout << "它没有左孩子.";if (pnode->rchild != nullptr)cout << "它的右孩子节点为:" << pnode->rchild->key << ".";else cout << "它没有右孩子.";cout << endl;print(pnode->lchild);print(pnode->rchild);}
};

3.5 哈夫曼树代码测试

我们构建上图中的哈夫曼树,它的四个权值分别为{10,20,30,40}:
测试代码:

int _tmain(int argc, _TCHAR* argv[])
{Huffman<int> huff;int a[] = { 10,20,30,40 };huff.creat(a, 4);    //构建一棵哈夫曼树huff.print();        //打印节点间关系getchar();return 0;
}

测试结果:

当前结点:100.它的左孩子节点为:40.它的右孩子节点为:60.
当前结点:40.它没有左孩子.它没有右孩子.
当前结点:60.它的左孩子节点为:30.它的右孩子节点为:30.
当前结点:30.它没有左孩子.它没有右孩子.
当前结点:30.它的左孩子节点为:10.它的右孩子节点为:20.
当前结点:10.它没有左孩子.它没有右孩子.
当前结点:20.它没有左孩子.它没有右孩子.

根据节点关系可以画出如下二叉树,正是上面我们构建的哈夫曼树。

4. 再看哈夫曼编码

为{10,20,30,40}这四个权值构建了哈夫曼编码后,我们可以由如下规则获得它们的哈夫曼编码:

  • 从根节点到每一个叶子节点的路径上,左分支记为0,右分支记为1,将这些0与1连起来即为叶子节点的哈夫曼编码。如下图:

(字母)权值 编码
10 100
20 101
30 11
40 0

由此可见,出现频率越高的字母(也即权值越大),其编码越短。这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。

5. 哈夫曼树完整代码

哈夫曼树完整代码:https://github.com/huanzheWu/Data-Structure/blob/master/Huffman/Huffman/Huffman.h
更多数据结构C++实现代码:https://github.com/huanzheWu/Data-Structure

原创文章,转载请注明出处:http://www.cnblogs.com/QG-whz/p/5175485.html

转载于:https://www.cnblogs.com/QG-whz/p/5175485.html

数据结构图文解析之:哈夫曼树与哈夫曼编码详解及C++模板实现相关推荐

  1. 【数据结构与算法】4.数据结构图文解析系列

    数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 数据结 ...

  2. Python数据结构11:树的实现,树的应用,前中后序遍历,二叉查找树BST,平衡二叉树AVL树,哈夫曼树和哈夫曼编码

    1.概念 树一种基本的"非线性"数据结构. 相关术语: 节点Node:组成树的基本部分.每个节点具有名称,或"键值",节点还可以保存额外数据项,数据项根据不同的 ...

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

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

  4. 【数据结构】树与树的表示、二叉树存储结构及其遍历、二叉搜索树、平衡二叉树、堆、哈夫曼树与哈夫曼编码、集合及其运算

    1.树与树的表示 什么是树? 客观世界中许多事物存在层次关系 人类社会家谱 社会组织结构 图书信息管理 分层次组织在管理上具有更高的效率! 数据管理的基本操作之一:查找(根据某个给定关键字K,从集合R ...

  5. 【Java数据结构与算法】第十二章 哈夫曼树和哈夫曼编码

    第十二章 哈夫曼树和哈夫曼编码 文章目录 第十二章 哈夫曼树和哈夫曼编码 一.哈夫曼树 1.基本术语 2.构建思路 3.代码实现 三.哈夫曼编码 1.引入 2.介绍 3.代码实现哈夫曼编码综合案例 一 ...

  6. 【数据结构】-哈夫曼树以及哈夫曼编码

    哈夫曼树的几个定义 哈夫曼树又叫最优二叉树:特点是带权路径最短 带权路径长度:该结点到根结点的路径长度乘以该结点的权值. 树的带权路径长度(WPL):所有叶子结点到根结点的带全路径长度之和. 最优二叉 ...

  7. 【数据结构】【哈夫曼树】哈夫曼树、赫夫曼树(Huffman Tree)C语言实现

    目录 一.哈夫曼树定义与原理 二.构建哈夫曼树 三.哈夫曼编码 完整代码: 前言:章末含c语言实现完整代码 一.哈夫曼树定义与原理 哈夫曼树又称最优二叉树,是一种带权路径长度最短的二叉树.所谓树的带权 ...

  8. 一文看懂哈夫曼树与哈夫曼编码

    转自:http://www.cnblogs.com/Jezze/archive/2011/12/23/2299884.html 在一般的数据结构的书中,树的那章后面,著者一般都会介绍一下哈夫曼(HUF ...

  9. 树:哈夫曼树和哈夫曼编码的详细介绍以及代码实现

    闲扯前言 哈夫曼编码的代码实现对于初学数据结构的同学可能会有些困难,没有必要灰心,其实没啥,学习就犹如攀登一座又一座的山峰,每当我们攻克一个难点后,回首来看,也不过如此嘛.我们要做的就是不断的去攀越学 ...

最新文章

  1. Conversion error:Jekyll::Converters::Scss encountered an error while converting css/main.scss
  2. 解决微信H5获取SDK授权报错提示errMsg: “config:fail,Error: 系统错误,错误码:63002,invalid signature [20200908 22:17:17][]“
  3. 一步一图一代码之排序二叉树
  4. dhcp协议_DHCP(动态主机分配协议)原理
  5. 深入学习keepalived之预备工作--线程
  6. 抽取python 标准库页面生成 mobi 离线文件
  7. 网络安全08-虚拟机运行架构(寄居架构+原生架构)、虚拟机产品简单介绍、windows操作系统--屏蔽系统自动更新
  8. SSH项目中根据Hibernate的映射文件生成数据库表的方案:
  9. windows route netstat arp命令
  10. 信息学奥赛一本通 1958:【12NOIP普及组】寻宝 | OpenJudge NOI 1.12 06 | 洛谷 P1076 [NOIP2012 普及组] 寻宝
  11. Facebook 数据的横向扩展
  12. Spring Boot-------JPA——EntityManager构建通用DAO
  13. BackgroundWorker的使用
  14. 如何激活Office 2016(ProPlus/Visio2016/Project2016) VOL 简体中文版下载地址和安装方法哦
  15. 【数字电路】集成逻辑门电路
  16. mac电脑chrome截长图
  17. 使用 python-pptx-interface 将PPT转换成图片
  18. 各个小组对于“我爱淘”的评价
  19. 你的私密照片可能正被“合法”观看
  20. 服务器2016自动备份怎么取消,wps中ppt的制作怎样取消掉定时自动备份

热门文章

  1. Ubuntu安装Docker引擎和支持HTTPS的docker-registry服务
  2. openstack 手动安装版 功能测试
  3. icmp的报文,Destination Host Unreachable
  4. #pragma once 和 #ifndef ... #define ... #endif 的区别
  5. 如何从菜鸟成长为高手!
  6. 软件测试工程师-计算机基础
  7. devops_面向内向的人的DevOps
  8. python scikit_如何将Scikit学习Python库用于数据科学项目
  9. oxp开放型可变长协议_培养开放型领导者
  10. 脚本化HTTP 取得响应 指定请求