本文的内容是如何通过二叉树实现一个最大堆, 实现原理方面参考了Python的heap模块. 此外, 在正式项目上, 我还是建议你使用python自带的heap完成, 它只提供最小堆, 但是可以通过对所有元素取反或者重写__lt__方法实现最大堆.

一. 堆的数据结构

1. 数据结构分析

堆的本质就是一颗二叉树, 这颗二叉树必须具备以下两个性质:

1). 对于最大堆来说, 二叉树根节点的值不小于任何子节点, 其所有子树也符合这一特征, 最小堆则相反;

2). 堆是一颗完全二叉树, 除了底层外, 所有层都尽可能地填满, 底层元素从左到右排列.

上图就是一个最大堆的二叉树, 基于特性1我们可以得知, 这颗二叉树从任意叶子节点到根节点的路径一定是一个递增序列, 最大值为根节点. 因此, 当我们需要最大值时, 取出根节点的值就行了. 当我们新添加了一个叶子节点之后, 为了维护二叉树的有序性, 我们可以让这个叶子节点向顶端移动, 如下图所示:

->

->

我们插入节点16后, 将这个节点的值与其父节点进行比较, 大于父节点则二者交换, 持续这个操作直到不大于父节点或没有父节点为止, 这样, 我们就在插入元素之后, 仍然保持了二叉树的有序性. 弹出节点同理, 将底层最后一个叶子节点取出填入空缺, 然后根据值的大小让这个节点往下移动就行.

因此, 堆在保证内部有序性的前提下, 可以做到在O(k)的时间内插入和弹出元素, k为二叉树的高度. 这也就是为什么堆的二叉树必须是完全二叉树: 在这种情况下k最小, 为log n. 因此, 堆的插入和弹出都只需要O(log n)的时间复杂度, 可以高效地获取最大值/最小值.

2. 通过列表实现二叉树

由于堆是一颗完全二叉树, 因此我们可以用一个列表来储存这颗二叉树的值:

如上图所示, 我们用列表从上到下, 从左到右记录了二叉树的所有节点. 二叉树节点右边的蓝色数字是它在列表中的索引. 因此我们可以得知, 对于一个在列表中索引为n的节点, 它的父节点索引为(n-1)//2, 它的左右子节点索引为n*2+1和n*2+2, 如果索引值溢出, 说明没有对应的父节点或子节点. 这样, 我们就通过列表储存了这颗完全二叉树的信息.

基于以上的分析, 我们先定义一个Heap类:

classHeap:def __init__(self, nums: [int] = None) ->None:

self.cache= nums or[]

self._heapify()def __len__(self) ->int:returnlen(self.cache)def __bool__(self) ->bool:return len(self) >0def __repr__(self) ->str:return f'heap({self.cache})'@propertydef largest(self) ->int:if notself.cache:raise Exception('Empty heap')returnself.cache[0]def show(self) ->None:#调用这个函数绘制一颗二叉树出来,DEBUG用

height = int(math.log2(len(self))) + 1

for i inrange(height):

width= 2 ** (height - i) - 2

print(' ' * width, end='')

blank= ' ' * (width * 2 + 2)print(

blank.join(['{: >2d}'.format(num) for num in self.cache[2 ** i - 1:min(2 ** (i + 1) - 1, len(self))]]))print()def _swap(self, i: int, j: int) ->None:#这个方法交换二叉树的两个节点

self.cache[i], self.cache[j] = self.cache[j], self.cache[i]

二. 插入元素

这部分好像太简单了, 我实在讲不出来什么:

def push(self, num: int) ->None:

self.cache.append(num)

self._siftup(self.size- 1)def _siftup(self, i: int) ->None:while i >0:

parent= (i - 1) >> 1

if self.cache[i] <=self.cache[parent]:breakself._swap(i, parent)

i= parent

说白了, 当我们push一个元素时, 首先把这个元素放到列表的末端, 这相当于在完全二叉树上新建了一个叶子节点. 然后, 调用siftup方法让这个节点一直和父节点比较, 大于父节点就上浮, 直到它到达合适的位置. 这样就维护了二叉树的有序性.

三. 弹出元素

弹出元素的原理和插入元素大同小异: 我们将根节点的元素弹出后, 取出最后一个叶子节点作为根节点(避免破坏完全二叉树的结构), 然后让这个节点与子节点比较, 下沉到合适的位置就行. 有两点需要注意一下: 首先, 最大元素处在列表的头部, 弹出的时间复杂度是O(n), 因此我们可以把头部元素和尾部元素交换后, 删除尾部元素. 然后, 大部分节点都有两个子节点, 我们应该让更大的那个节点上浮, 这样才能保证二叉树的有序性.

基于以上两点, 弹出元素的代码如下:

def pop(self) ->int:

largest=self.largest

self._swap(0, len(self)- 1)

self.cache.pop()

self._siftdown(0)returnlargestdef _siftdown(self, i: int) ->None:while i * 2 + 1

smaller=iif self.cache[i * 2 + 1] >self.cache[smaller]:

smaller= i * 2 + 1

if i * 2 + 2 < len(self) and self.cache[i * 2 + 2] >self.cache[smaller]:

smaller= i * 2 + 2

if smaller ==i:returnself._swap(i, smaller)

i= smaller

四. 列表的堆化

我们在创建Heap对象时传入了一个列表作为堆的原始数据, 但是, 这个列表并不一定是颗有序的二叉树, 因此我们需要将其堆化.

最容易想到的方式是, 首先创建一个空堆, 然后将列表的所有元素依次推入堆中, 通过_siftup方法保持有序:

如上图所示, 如果我们通过_siftup来堆化所有元素, 则时间复杂度为O(n/2*log n+n/4*log n/2+...+1*1)=O(nlog n), 这和排序的时间复杂度差不多, 因此不是很理想.

另外一种方案是, 首先按照列表的原有顺序构建二叉树, 然后从二叉树的倒数第二层开始, 依次通过_siftdown下沉, 这样依次为k-1层, k-2层直到顶层排序:

这种堆化方式的时间复杂度为O(n), 计算过程如下:

T(n)=O(n/4)+O(n/8*2)+(n/16*3)+O(log n)2*T(n)=O(n/2)+O(n/4*2)+(n/8*3)+O(2*log n)2*T(n)-T(n)=O(n/2)+O(n/4)+O(n/8)+...+O(log n)=O(n)

因此, 我们的堆化方法可以这么写:

def _heapify(self) ->None:for i in reversed(range(len(self) // 2)):

self._siftdown(i)

五. 总结

简单对我们创建的Heap类进行测试:

nums = list(range(14))

random.shuffle(nums)

heap=Heap(nums[:])

heap.show()

heap.push(100)print('插入100')

heap.show()

heap.pop()print('弹出堆顶元素')

heap.show()for _ in range(100):

num= random.randrange(100)

nums.append(num)

heap.push(num)assert max(nums) ==heap.largest

nums.remove(heap.pop())print('所有测试通过!!!')

结果如下:

python最大堆_用Python实现最大堆相关推荐

  1. python 时间序列预测_使用Python进行动手时间序列预测

    python 时间序列预测 Time series analysis is the endeavor of extracting meaningful summary and statistical ...

  2. python 概率分布模型_使用python的概率模型进行公司估值

    python 概率分布模型 Note from Towards Data Science's editors: While we allow independent authors to publis ...

  3. python集群_使用Python集群文档

    python集群 Natural Language Processing has made huge advancements in the last years. Currently, variou ...

  4. python 网页编程_通过Python编程检索网页

    python 网页编程 The internet and the World Wide Web (WWW), is probably the most prominent source of info ...

  5. python机器学习预测_使用Python和机器学习预测未来的股市趋势

    python机器学习预测 Note from Towards Data Science's editors: While we allow independent authors to publish ...

  6. python小型编程_学习Python编程的11个资源

    用 Python 写代码并不难,事实上,它一直以来都是被声称为最容易学习的编程语言.如果你正打算学习 web 开发,Python 是一个不错的选择,甚至你想学游戏开发也可 以从 Python 开始,因 ...

  7. python高斯求和_利用Python进行数据分析(3)- 列表、元组、字典、集合

    本文主要是对Python的数据结构进行了一个总结,常见的数据结构包含:列表list.元组tuple.字典dict和集合set. image 索引 左边0开始,右边-1开始 通过index()函数查看索 ...

  8. python 免费空间_用python做大数据

    不学Python迟早会被淘汰?Python真有这么好的前景? 最近几年Python编程语言在国内引起不小的轰动,有超越Java之势,本来在美国这个编程语言就是最火的,应用的非常非常的广泛,而Pytho ...

  9. python希腊字母字符串_#10 Python字符串

    前言 通过上一节可知,Python6个序列的内置类型中,最常见的是列表和元组,但在Python中,最常用的数据类型却不是列表和元组,而是字符串.要想深入了解字符串,必须先掌握字符编码问题.因此本篇博文 ...

  10. python ray定时_当 Python 邂逅 POV-Ray

    引言 POV-Ray 是一种专业的三维场景描述语言,它描述的三维场景可交由 POV-Ray 的解析器(或编译器)采用光线跟踪技术进行渲染,渲染结果为位图. POV-Ray 语言是图灵完备的,亦即其他编 ...

最新文章

  1. 2020年虚拟现实和增强现实的发展趋势将会如何|0glasses低调分享
  2. 【组合数学】生成函数 ( 正整数拆分 | 无序不重复拆分示例 )
  3. 程序员面试题精选100题(38)-输出1到最大的N位数[算法]
  4. cass字体_不动产 准备工作 第一步: 管理CASS码
  5. 唐山师范学院计算机论文,唐山师范学院校园网络解决方案 毕业论文
  6. 大数据之-入门_大数据部门业务流程分析---大数据之hadoop工作笔记0006
  7. Android layoutInflate.inflate 方法具体解释,removeView()错误解决
  8. 1.1 Mysql安装包 windows
  9. discuz admin.php无法登录,Discuz x3.1论坛管理员无法登录后台的各种解决方法总结
  10. 软件定义汽车下的整车开发
  11. 异数OS-织梦师-异数OS虚拟容器交换机(七) 走进4Tbps网络应用时代,加速5G应用真正落地
  12. 【Python计量】内生性问题、工具变量法与二阶段最小二乘法2SLS
  13. Office Web Add-in的技术原理和开发常见问题剖析
  14. Web报表系统葡萄城报表:报表设计
  15. STM32F103RCT+TJA1050+USBCAN盒做can通讯
  16. Qt开发高级进阶:如何拷贝生成后的文件到特定文件夹
  17. 视频打赏/付费视频引流吸粉/定时弹窗广告+自带视频+支付接口+自适应设计/带安装教程
  18. 【原创】PHP扩展开发入门
  19. 2021年危险化学品生产单位安全生产管理人员最新解析及危险化学品生产单位安全生产管理人员作业模拟考试
  20. python接管已经打开ie浏览器_使用selenium控制(接管)已打开的浏览器(chrome),并通过WebDriver值检测...

热门文章

  1. php svn up,php中执行svn update问题
  2. java日志服务器_java服务器搭建(一)日志系统
  3. 深度学习DL蒙特卡洛法平衡态分子动力学模拟并计算苯酚键值
  4. 【控制】《多智能体系统的协同群集运动控制》陈杰老师-第11章-基于邻居相关状态的多智能体非合作行为检测与隔离
  5. 大总结-深度学习全五课-Stanford吴恩达教授
  6. 1.8 为什么是人的表现-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  7. 【PC工具】chrome插件:n多好用实用chrome插件
  8. BRCM5.02编译九:cannot find -lncurses
  9. 【强化学习】Q-Learning
  10. 【共振峰跟踪】通过平均不同分辨率的方法跟踪共振峰,基于时频lpc的频谱图的MATLAB仿真