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 相关文章推荐
pydev使用wxpython找不到路径的解决方法
Feb 10 Python
Python中的zipfile模块使用详解
Jun 25 Python
Django应用程序中如何发送电子邮件详解
Feb 04 Python
Python设计模式之代理模式简单示例
Jan 09 Python
Python爬虫实现百度图片自动下载
Feb 04 Python
使用NumPy和pandas对CSV文件进行写操作的实例
Jun 14 Python
Python XlsxWriter模块Chart类用法实例分析
Mar 11 Python
利用Python+阿里云实现DDNS动态域名解析的方法
Apr 01 Python
从列表或字典创建Pandas的DataFrame对象的方法
Jul 06 Python
opencv python图像梯度实例详解
Feb 04 Python
python opencv实现图片缺陷检测(讲解直方图以及相关系数对比法)
Apr 07 Python
学python最电脑配置有要求么
Jul 05 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和ACCESS写聊天室(九)
2006/10/09 PHP
PHP删除HTMl标签的实现代码
2013/06/30 PHP
PHP CURL获取cookies模拟登录的方法
2013/11/04 PHP
PHP防范SQL注入的具体方法详解(测试通过)
2014/05/09 PHP
PHP实现对站点内容外部链接的过滤方法
2014/09/10 PHP
fsockopen pfsockopen函数被禁用,SMTP发送邮件不正常的解决方法
2015/09/20 PHP
laravel-admin 在列表页添加自定义按钮的例子
2019/09/30 PHP
laravel 实现用户登录注销并限制功能
2019/10/24 PHP
Javascript 不能释放内存.
2006/09/07 Javascript
用Javascript做flash做的事..才完成的一个类.Auntion Action var 0.1
2007/02/23 Javascript
EasyUI中的tree用法介绍
2011/11/01 Javascript
offsetHeight在OnLoad中获取为0的现象
2013/07/22 Javascript
使用jquery获取网页中图片高度的两种方法
2013/09/26 Javascript
javascript 控制input只允许输入的各种指定内容
2014/06/19 Javascript
JavaScript实现在数组中查找不同顺序排列的字符串
2014/09/26 Javascript
了不起的node.js读书笔记之node的学习总结
2014/12/22 Javascript
js限制文本框只能输入整数或者带小数点的数字
2015/04/27 Javascript
浅谈bootstrap源码分析之tab(选项卡)
2016/06/06 Javascript
归纳下js面向对象的几种常见写法总结
2016/08/24 Javascript
浅谈jquery拼接字符串效率比较高的方法
2017/02/22 Javascript
基于Vue2x的图片预览插件的示例代码
2018/05/14 Javascript
JS字典Dictionary类定义与用法示例
2019/02/01 Javascript
基于mpvue的简单弹窗组件mptoast使用详解
2019/08/02 Javascript
vue 实现强制类型转换 数字类型转为字符串
2019/11/07 Javascript
JavaScript函数重载操作实例浅析
2020/05/02 Javascript
Linux下Pycharm、Anaconda环境配置及使用踩坑
2018/12/19 Python
PyQt 图解Qt Designer工具的使用方法
2019/08/06 Python
HTML5如何实现元素拖拽
2016/03/11 HTML / CSS
自动化专业毕业生自荐信
2013/11/01 职场文书
工业设计专业自荐书
2014/06/05 职场文书
迁户口计划生育证明
2014/10/19 职场文书
财务总监岗位职责
2015/02/03 职场文书
三八节活动简报
2015/07/20 职场文书
2016中学教师读书心得体会
2016/01/13 职场文书
《自己去吧》教学反思
2016/02/16 职场文书
Oracle11g r2 卸载干净重装的详细教程(亲测有效已重装过)
2021/06/04 Oracle