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编写简单网络爬虫抓取视频下载资源
Nov 04 Python
详解Python程序与服务器连接的WSGI接口
Apr 29 Python
python fabric使用笔记
May 09 Python
python类的继承实例详解
Mar 30 Python
Python实现运行其他程序的四种方式实例分析
Aug 17 Python
Python搭建FTP服务器的方法示例
Jan 19 Python
Python及Django框架生成二维码的方法分析
Jan 31 Python
Python的Tkinter点击按钮触发事件的例子
Jul 19 Python
python从list列表中选出一个数和其对应的坐标方法
Jul 20 Python
python线程中的同步问题及解决方法
Aug 29 Python
Python搭建代理IP池实现获取IP的方法
Oct 27 Python
Python基于pillow库实现生成图片水印
Sep 14 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
Drupal简体中文语言包安装教程
2014/09/27 PHP
php中使用base HTTP验证的方法
2015/04/20 PHP
php验证身份证号码正确性的函数
2016/07/20 PHP
PHP递归遍历文件夹去除注释并压缩php源代码的方法示例
2018/05/23 PHP
一端时间轮换的广告
2006/06/26 Javascript
颜色选择器 Color Picker,IE,Firefox,Opera,Safar
2010/11/25 Javascript
jquery miniui 教程 表格控件 合并单元格应用
2012/11/25 Javascript
javasciprt下jquery函数$.post执行无响应的解决方法
2014/03/13 Javascript
jquery+ajax实现跨域请求的方法
2015/01/20 Javascript
jQuery实现“扫码阅读”功能
2015/01/21 Javascript
@ResponseBody 和 @RequestBody 注解的区别
2017/03/08 Javascript
vue计算属性时v-for处理数组时遇到的一个bug问题
2018/01/21 Javascript
Vue实现带进度条的文件拖动上传功能
2018/02/23 Javascript
Koa代理Http请求的示例代码
2018/10/10 Javascript
小程序自定义模板实现吸顶功能
2020/01/08 Javascript
vue项目实现减少app.js和vender.js的体积操作
2020/11/12 Javascript
详解Python当中的字符串和编码
2015/04/25 Python
用Python下载一个网页保存为本地的HTML文件实例
2018/05/21 Python
python实现三维拟合的方法
2018/12/29 Python
解决django后台样式丢失,css资源加载失败的问题
2019/06/11 Python
python爬虫神器Pyppeteer入门及使用
2019/07/13 Python
Python中xml和dict格式转换的示例代码
2019/11/07 Python
关于tf.reverse_sequence()简述
2020/01/20 Python
python数据预处理方式 :数据降维
2020/02/24 Python
Django框架models使用group by详解
2020/03/11 Python
Opencv求取连通区域重心实例
2020/06/04 Python
Django QuerySet查询集原理及代码实例
2020/06/13 Python
怎样从/向数据文件读/写结构
2014/11/23 面试题
ShellScript面试题一则-ShellScript编程
2014/03/05 面试题
【魔兽争霸3重制版】原版画面与淬火MOD画面对比
2021/03/26 魔兽争霸
银行会计职员个人的自我评价
2013/09/29 职场文书
楼面经理岗位职责范本
2014/02/18 职场文书
写给老婆的检讨书
2014/02/21 职场文书
人事行政专员岗位职责
2014/07/23 职场文书
学校党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
幼儿园科学课教学反思
2016/03/03 职场文书