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脚本实现下载合并SAE日志
Feb 10 Python
Python中decorator使用实例
Apr 14 Python
python中字符串变二维数组的实例讲解
Apr 03 Python
Django压缩静态文件的实现方法详析
Aug 26 Python
python简易实现任意位数的水仙花实例
Nov 13 Python
python实现统计代码行数的小工具
Sep 19 Python
详解Python3迁移接口变化采坑记
Oct 11 Python
Python使用enumerate获取迭代元素下标
Feb 03 Python
python实现发送邮件
Mar 02 Python
python 如何用map()函数创建多线程任务
Apr 07 Python
python基础之文件操作
Oct 24 Python
Python中的 Set 与 dict
Mar 13 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设计模式 Facade(外观模式)
2011/06/26 PHP
ThinkPHP CURD方法之where方法详解
2014/06/18 PHP
php实现使用正则将文本中的网址转换成链接标签
2014/12/03 PHP
ThinkPHP防止重复提交表单的方法实例分析
2018/05/10 PHP
PHP设计模式(五)适配器模式Adapter实例详解【结构型】
2020/05/02 PHP
Javascript判断对象是否相等实现代码
2013/03/18 Javascript
window.close(); 关闭浏览器窗口js代码的总结介绍
2016/07/14 Javascript
AngularJS API之copy深拷贝详解及实例
2016/09/14 Javascript
Node.js connect ECONNREFUSED错误解决办法
2016/09/15 Javascript
BootStrap Table 设置height表头与内容无法对齐的问题
2016/12/28 Javascript
js获取地址栏中传递的参数(两种方法)
2017/02/08 Javascript
angular+ionic返回上一页并刷新页面
2017/08/08 Javascript
微信小程序实现滚动消息通知
2018/02/02 Javascript
vue2.0 根据状态值进行样式的改变展示方法
2018/03/13 Javascript
Vue + Elementui实现多标签页共存的方法
2019/06/12 Javascript
微信小程序日历插件代码实例
2019/12/04 Javascript
[50:44]DOTA2-DPC中国联赛 正赛 SAG vs Dragon BO3 第二场 2月22日
2021/03/11 DOTA
Python 命令行参数sys.argv
2008/09/06 Python
python SSH模块登录,远程机执行shell命令实例解析
2018/01/12 Python
python pandas.DataFrame选取、修改数据最好用.loc,.iloc,.ix实现
2018/06/11 Python
python实现俄罗斯方块游戏
2020/03/25 Python
解读python如何实现决策树算法
2018/10/11 Python
选择Python写网络爬虫的优势和理由
2019/07/07 Python
python 默认参数相关知识详解
2019/09/18 Python
Python3 使用selenium插件爬取苏宁商家联系电话
2019/12/23 Python
为什么说python更适合树莓派编程
2020/07/20 Python
Herve Leger官网:标志性绷带连衣裙等
2018/12/26 全球购物
彪马法国官网:PUMA法国
2019/12/15 全球购物
Linux Interview Questions For software testers
2012/06/02 面试题
在求职信中如何凸显个人优势
2013/10/30 职场文书
学年自我鉴定
2014/01/16 职场文书
青春奉献演讲稿
2014/05/08 职场文书
搞笑的获奖感言
2014/08/16 职场文书
财务总监岗位职责
2015/02/03 职场文书
出生证明格式
2015/06/15 职场文书
同学会感言
2015/07/30 职场文书