Python描述数据结构学习之哈夫曼树篇


Posted in Python onSeptember 07, 2020

前言

本篇章主要介绍哈夫曼树及哈夫曼编码,包括哈夫曼树的一些基本概念、构造、代码实现以及哈夫曼编码,并用Python实现。

1. 基本概念

哈夫曼树(Huffman(Huffman(Huffman Tree)Tree)Tree),又称为最优二叉树,指的是带权路径长度最小的二叉树。树的带权路径常记作:

Python描述数据结构学习之哈夫曼树篇

其中,nnn为树中叶子结点的数目,wkw_kwk​为第kkk个叶子结点的权值,lkl_klk​为第kkk个叶子结点与根结点的路径长度。

带权路径长度是带权结点和根结点之间的路径长度与该结点的权值的乘积。有关带权结点、路径长度的概念请参阅这篇博客。

对于含有nnn个叶子结点的哈夫曼树,其共有2n12n-12n−1个结点。因为在构造哈夫曼树的过程中,每次都是以两颗二叉树为子树创建一棵新的二叉树,因此哈夫曼树中不存在度为1的结点,即n1=0n_1=0n1​=0,由二叉树的性质可知,叶子结点数目n0=n2+1n_0=n_2+1n0​=n2​+1,所以n2=n01n_2=n_0-1n2​=n0​−1,总结点数目为n=n0+n1+n2=n+n1=2n1n=n_0+n_1+n_2=n+n-1=2n-1n=n0​+n1​+n2​=n+n−1=2n−1。

2. 构造过程及实现

给定nnn棵仅含根结点的二叉树T1,T2,,TnT_1,T_2,\dots,T_nT1​,T2​,…,Tn​,它们的权值分别为w1,w2,,wnw_1,w_2,\dots,w_nw1​,w2​,…,wn​,将它们放入到一个集合FFF中,即F={T1,T2,,Tn}F=\{T_1,T_2,\dots,T_n\}F={T1​,T2​,…,Tn​};然后在集合FFF中选取两棵权值最小的根结点构造一棵新的二叉树,使新二叉树的根结点的权值等于其左、右子树根结点的权值之和;再然后将选中的那两个结点从集合FFF中删除,将新的二叉树添加到FFF中;继续重复上述操作,直至集合FFF中只剩一棵二叉树为止。

比如F={(A,3),(B,7),(C,2),(D,11),(E,13),(F,15),(G,9)}F=\{(A,3),(B,7),(C,2),(D,11),(E,13),(F,15),(G,9)\}F={(A,3),(B,7),(C,2),(D,11),(E,13),(F,15),(G,9)},它构造出来的哈夫曼树就是下面这棵二叉树:

Python描述数据结构学习之哈夫曼树篇

代码实现:

class HuffmanTreeNode(object):
 def __init__(self):
 self.data = '#'
 self.weight = -1
 self.parent = None
 self.lchild = None
 self.rchild = None


class HuffmanTree(object):
 def __init__(self, data_list):
 self.nodes = []
 # 按权重从大到小进行排列
 for val in data_list:
  newnode = HuffmanTreeNode()
  newnode.data = val[0]
  newnode.weight = val[1]
  self.nodes.append(newnode)
 self.nodes = sorted(self.nodes, key=lambda node: node.weight, reverse=True)
 print([(node.data, node.weight) for node in self.nodes])

 def CreateHuffmanTree(self):
 # 这里注意区分
 # TreeNode = self.nodes[:] 变量TreeNode, 这个相当于深拷贝, TreeNode变化不影响nodes
 # TreeNode = self.nodes 指针TreeNode与nodes共享一个地址, 相当于浅拷贝, TreeNode变化会影响nodes
 TreeNode = self.nodes[:]
 if len(TreeNode) > 0:
  while len(TreeNode) > 1:
  letfTreeNode = TreeNode.pop()
  rightTreeNode = TreeNode.pop()
  newNode = HuffmanTreeNode()
  newNode.lchild = letfTreeNode
  newNode.rchild = rightTreeNode
  newNode.weight = letfTreeNode.weight + rightTreeNode.weight
  letfTreeNode.parent = newNode
  rightTreeNode.parent = newNode
  self.InsertTreeNode(TreeNode, newNode)
  return TreeNode[0]

 def InsertTreeNode(self, TreeNode, newNode):
 length = len(TreeNode)
 if length > 0:
  temp = length - 1
  while temp >= 0:
  if newNode.weight < TreeNode[temp].weight:
   TreeNode.insert(temp+1, newNode)
   return True
  temp -= 1
 TreeNode.insert(0, newNode)

3. 哈夫曼编码

在数据通信时,假如我们要发送ABCDEFG”“ABCDEFG”“ABCDEFG”这一串信息,我们并不会直接以这种形式进行发送,而是将其编码成计算机能够识别的二进制形式。根据编码类型可将其分为固定长度编码和可变长度编码,顾名思义,固定长度编码就是编码后的字符长度都相同,可变长度编码就是编码后的字符长度不相同。这两种类型有什么区别呢?我们来举例说明一下:

AA BB CC DD EE FF GG
固定长度编码 000000 001001 010010 011011 100100 101101 110110
可变长度编码 00 11 0101 1010 1111 101101 110110

ABCDEFG”“ABCDEFG”“ABCDEFG”这条信息使用固定长度编码后的长度为21,使用可变长度编码后的长度为14,报文变短,报文的传输效率会相应的提高。但如果传送的字符为BD”“BD”“BD”,按可变长度编码后的报文为111”“111”“111”,但是在译码是就会出现BBB,BD,DB”“BBB”,“BD”,“DB”“BBB”,“BD”,“DB”多种结果,因此采用可变长度编码时需要注意任一字符不能是其他字符的前缀,符合这样的可变长度编码称为前缀编码。

报文最短可以引申到二叉树路径最短,即构造前缀编码的实质就是构造一棵哈夫曼树,通过这种形式获得的二进制编码称为哈夫曼编码。这里的权值就是报文中字符出现的概率,出现概率越高的字符我们用越短的字符表示。

以下表中的字符及其出现的概率为例来实现哈夫曼编码:

字符 AA BB CC DD EE FF GG HH
出现概率 0.010.01 0.430.43 0.150.15 0.020.02 0.030.03 0.210.21 0.070.07 0.08
哈夫曼编码 101010101010 00 110110 101011101011 1010010100 111111 10111011 100

Python描述数据结构学习之哈夫曼树篇

代码实现就是在哈夫曼树的基础上加一个编码的函数:

def HuffmanEncode(self, Root):
  TreeNode = self.nodes[:]
  code_result = []
  for index in range(len(TreeNode)):
   temp = TreeNode[index]
   code_leaf = [temp.data]
   code = ''
   while temp is not Root:
    if temp.parent.lchild is temp:
     # 左分支
     code = '0' + code
    else:
     # 右分支
     code = '1' + code
    temp = temp.parent
   code_leaf.append(code)
   code_result.append(code_leaf)
  return code_result

测试结果如下:

if __name__ == '__main__':
 tree_obj = HuffmanTree([('A', 0.01), ('B', 0.43), ('C', 0.15), ('D', 0.02), ('E', 0.03), ('F', 0.21), ('G', 0.07), ('H', 0.08)])
 huf_tree = tree_obj.CreateHuffmanTree()
 huf_code = tree_obj.HuffmanEncode(huf_tree)
 for index in range(len(huf_code)):
  print('{0}: {1}'.format(huf_code[index][0], huf_code[index][1]))

Python描述数据结构学习之哈夫曼树篇

总结

到此这篇关于Python描述数据结构学习之哈夫曼树篇的文章就介绍到这了,更多相关Python数据结构之哈夫曼树内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
Python随机数用法实例详解【基于random模块】
Apr 18 Python
使用python实现knn算法
Dec 20 Python
Python实现将json文件中向量写入Excel的方法
Mar 26 Python
python实现excel读写数据
Mar 02 Python
如何使用 Pylint 来规范 Python 代码风格(来自IBM)
Apr 06 Python
Python解析并读取PDF文件内容的方法
May 08 Python
PyTorch读取Cifar数据集并显示图片的实例讲解
Jul 27 Python
解决新django中的path不能使用正则表达式的问题
Dec 18 Python
python3+django2开发一个简单的人员管理系统过程详解
Jul 23 Python
keras模型保存为tensorflow的二进制模型方式
May 25 Python
python openpyxl模块的使用详解
Feb 25 Python
解析目标检测之IoU
Jun 26 Python
python简单利用字典破解zip文件口令
Sep 07 #Python
python 如何快速复制序列
Sep 07 #Python
Python2与Python3关于字符串编码处理的差别总结
Sep 07 #Python
python 装饰器的实际作用有哪些
Sep 07 #Python
通俗讲解python 装饰器
Sep 07 #Python
彻底搞懂python 迭代器和生成器
Sep 07 #Python
python如何设置静态变量
Sep 07 #Python
You might like
PHP读取文件内容的五种方式
2015/12/28 PHP
游戏人文件夹程序 ver 4.03
2006/07/14 Javascript
在JavaScript中使用timer示例
2014/05/08 Javascript
javascript中Date对象的getDay方法使用指南
2014/12/22 Javascript
javascript中基本类型和引用类型的区别分析
2015/05/12 Javascript
基于MVC4+EasyUI的Web开发框架形成之旅之界面控件的使用
2015/12/16 Javascript
全面理解闭包机制
2016/07/11 Javascript
详解nodejs 文本操作模块-fs模块(一)
2016/12/22 NodeJs
JS正则验证多个邮箱完整实例【邮箱用分号隔开】
2017/04/19 Javascript
js制作简单的音乐播放器的示例代码
2017/08/28 Javascript
jQuery实现动态控制页面元素的方法分析
2017/12/20 jQuery
在Vue组件中使用 TypeScript的方法
2018/02/28 Javascript
Vue2.0 事件的广播与接收(观察者模式)
2018/03/14 Javascript
vue mounted组件的使用
2018/06/18 Javascript
微信小程序实现发送验证码按钮效果
2018/12/20 Javascript
layui前端时间戳转化实例
2019/11/15 Javascript
vue实现导航标题栏随页面滚动渐隐渐显效果
2020/03/12 Javascript
JS自定义右键菜单实现代码解析
2020/07/16 Javascript
vue移动端弹起蒙层滑动禁止底部滑动操作
2020/07/22 Javascript
python标准算法实现数组全排列的方法
2015/03/17 Python
和孩子一起学习python之变量命名规则
2018/05/27 Python
python生成n个元素的全组合方法
2018/11/13 Python
Python实现将HTML转成PDF的方法分析
2019/05/04 Python
python中使用while循环的实例
2019/08/05 Python
如何在Cloud Studio上执行Python代码?
2019/08/09 Python
python 实现turtle画图并导出图片格式的文件
2019/12/07 Python
python 第三方库paramiko的常用方式
2021/02/20 Python
trivago美国:全球最大的酒店价格比较网站
2018/01/18 全球购物
区域总监的岗位职责
2013/11/21 职场文书
银行实习鉴定
2013/12/13 职场文书
带病坚持工作事迹
2014/05/03 职场文书
2014年城管个人工作总结
2014/12/08 职场文书
教师自荐信范文
2015/03/06 职场文书
2015年政风行风工作总结
2015/04/21 职场文书
安全第一课观后感
2015/06/18 职场文书
浅析Python中的随机采样和概率分布
2021/12/06 Python