heapq-堆排序算法

heapq实现了一个适合与Python的列表一起使用的最小堆排序算法。

二叉树

树中每个节点至多有两个子节点

满二叉树

树中除了叶子节点,每个节点都有两个子节点

什么是完全二叉树

在满足满二叉树的性质后,最后一层的叶子节点均需在最左边

什么是堆?

堆是一种数据结构,它是一颗完全二叉树。最小堆则是在堆的基础增加了新的规则,它的根结点的值是最小的,而且它的任意结点的父结点的值都小于或者等于其左右结点的值。因为二进制堆可以使用有组织的列表或数组来表示,所以元素N的子元素位于位置2 * N + 1和2 * N + 2。这种布局使重新安排堆成为可能,因此在添加或删除项时不需要重新分配那么多内存

区分堆(heap)与栈(stack):堆与二叉树有关,像一堆金字塔型泥沙;而栈像一个直立垃圾桶,一列下来。

最大堆

最大堆确保父堆大于或等于它的两个子堆。

最小堆

最小堆要求父堆小于或等于其子堆。Python的heapq模块实现了一个最小堆。

创建一个堆

示例代码:

  1. heapq_heapdata.py
  2. # This data was generated with the random module.
  3. data = [19, 9, 4, 10, 11]

堆输出使用heapq showtree.py打印。

  1. heapq_showtree.py
  2. import math
  3. from io import StringIO
  4. def show_tree(tree, total_width=36, fill=' '):
  5. """Pretty-print a tree."""
  6. output = StringIO()
  7. last_row = -1
  8. for i, n in enumerate(tree):
  9. if i:
  10. row = int(math.floor(math.log(i + 1, 2)))
  11. else:
  12. row = 0
  13. if row != last_row:
  14. output.write('\n')
  15. columns = 2 ** row
  16. col_width = int(math.floor(total_width / columns))
  17. output.write(str(n).center(col_width, fill))
  18. last_row = row
  19. print(output.getvalue())
  20. print('-' * total_width)
  21. print()

这里有两种方案创建一个堆,一种是使用heappush(),一种是使用heapify()。

heappush

  1. heapq_heappush.py
  2. import heapq
  3. from heapq_showtree import show_tree
  4. from heapq_heapdata import data
  5. heap = []
  6. print('random :', data)
  7. print()
  8. for n in data:
  9. print('add {:>3}:'.format(n))
  10. heapq.heappush(heap, n)
  11. show_tree(heap)

使用heappush()时,当从数据源添加新项时,将维护元素的堆排序顺序。

  1. python3 heapq_heappush.py
  2. random : [19, 9, 4, 10, 11]
  3. add 19:
  4. 19
  5. ------------------------------------
  6. add 9:
  7. 9
  8. 19
  9. ------------------------------------
  10. add 4:
  11. 4
  12. 19 9
  13. ------------------------------------
  14. add 10:
  15. 4
  16. 10 9
  17. 19
  18. ------------------------------------
  19. add 11:
  20. 4
  21. 10 9
  22. 19 11
  23. ------------------------------------

如果数据已经在内存中,那么使用heapify()重新排列列表中的项会更有效。

heapify

  1. heapq_heapify.py
  2. import heapq
  3. from heapq_showtree import show_tree
  4. from heapq_heapdata import data
  5. print('random :', data)
  6. heapq.heapify(data)
  7. print('heapified :')
  8. show_tree(data)

按照堆顺序每次构建一项列表的结果与构建无序列表然后调用heapify()相同。

  1. $ python3 heapq_heapify.py
  2. random : [19, 9, 4, 10, 11]
  3. heapified :
  4. 4
  5. 9 19
  6. 10 11
  7. ------------------------------------

访问堆的内容

使用heappop()弹出并返回堆中的最小项,保持堆不变。如果堆是空的,则引发IndexError。

  1. heapq_heappop.py
  2. import heapq
  3. from heapq_showtree import show_tree
  4. from heapq_heapdata import data
  5. print('random :', data)
  6. heapq.heapify(data)
  7. print('heapified :')
  8. show_tree(data)
  9. print()
  10. for i in range(2):
  11. smallest = heapq.heappop(data)
  12. print('pop {:>3}:'.format(smallest))
  13. show_tree(data)

在本例中,使用heapify()和heappop()用于对数字列表进行排序。

  1. $ python3 heapq_heappop.py
  2. random : [19, 9, 4, 10, 11]
  3. heapified :
  4. 4
  5. 9 19
  6. 10 11
  7. ------------------------------------
  8. pop 4:
  9. 9
  10. 10 19
  11. 11
  12. ------------------------------------
  13. pop 9:
  14. 10
  15. 11 19
  16. ------------------------------------

要删除现有元素并用单个操作中的新值替换它们,请使用heapreplace()。

heapreplace

  1. heapq_heapreplace.py
  2. import heapq
  3. from heapq_showtree import show_tree
  4. from heapq_heapdata import data
  5. heapq.heapify(data)
  6. print('start:')
  7. show_tree(data)
  8. for n in [0, 13]:
  9. smallest = heapq.heapreplace(data, n)
  10. print('replace {:>2} with {:>2}:'.format(smallest, n))
  11. show_tree(data)

替换适当的元素可以维护固定大小的堆,比如按优先级排序的作业队列。

  1. $ python3 heapq_heapreplace.py
  2. start:
  3. 4
  4. 9 19
  5. 10 11
  6. ------------------------------------
  7. replace 4 with 0:
  8. 0
  9. 9 19
  10. 10 11
  11. ------------------------------------
  12. replace 0 with 13:
  13. 9
  14. 10 19
  15. 13 11
  16. ------------------------------------

堆中的数据极端值

heapq还包含两个函数,用于检查一个迭代器,并找到它所包含的最大或最小值的范围。

  1. heapq_extremes.py
  2. import heapq
  3. from heapq_heapdata import data
  4. print('all :', data)
  5. print('3 largest :', heapq.nlargest(3, data))
  6. print('from sort :', list(reversed(sorted(data)[-3:])))
  7. print('3 smallest:', heapq.nsmallest(3, data))
  8. print('from sort :', sorted(data)[:3])

使用nlargest()和nsmallest()仅对n> 1的相对较小的值有效,但在少数情况下仍然可以派上用场。

  1. $ python3 heapq_extremes.py
  2. all : [19, 9, 4, 10, 11]
  3. 3 largest : [19, 11, 10]
  4. from sort : [19, 11, 10]
  5. 3 smallest: [4, 9, 10]
  6. from sort : [4, 9, 10]

有效地合并排序Sequences

对于小数据集来说,将几个排序的序列组合成一个新的序列是很容易的。

  1. list(sorted(itertools.chain(*data)))

对于较大的数据集,这种技术可以使用相当大的内存。merge()不是对整个组合序列进行排序,而是使用堆每次生成一个新序列中的一个项,并使用固定数量的内存确定下一个项。

  1. heapq_merge.py
  2. import heapq
  3. import random
  4. random.seed(2016)
  5. data = []
  6. for i in range(4):
  7. new_data = list(random.sample(range(1, 101), 5))
  8. new_data.sort()
  9. data.append(new_data)
  10. for i, d in enumerate(data):
  11. print('{}: {}'.format(i, d))
  12. print('\nMerged:')
  13. for i in heapq.merge(*data):
  14. print(i, end=' ')
  15. print()

因为merge()的实现使用堆,所以它根据要合并的序列的数量而不是这些序列中的项的数量来消耗内存。

  1. $ python3 heapq_merge.py
  2. 0: [33, 58, 71, 88, 95]
  3. 1: [10, 11, 17, 38, 91]
  4. 2: [13, 18, 39, 61, 63]
  5. 3: [20, 27, 31, 42, 45]
  6. Merged:
  7. 10 11 13 17 18 20 27 31 33 38 39 42 45 58 61 63 71 88 91 95

上面是小根堆的相关操作。python的heapq不支持大根堆,在stackoverflow上看到了一个巧妙的实现:我们还是用小根堆来进行逻辑操作,在做push的时候,我们把最大数的相反数存进去,那么它的相反数就是最小数,仍然是堆顶元素,在访问堆顶的时候,再对它取反,就获取到了最大数。思路很是巧妙。下面是实现代码

  1. class BigHeap:
  2. def init(self):
  3. self.arr = list()
  4. def heap_insert(self, val):
  5. heapq.heappush(self.arr, -val)
  6. def heapify(self):
  7. heapq.heapify(self.arr)
  8. def heap_pop(self):
  9. return -heapq.heappop(self.arr)
  10. def get_top(self):
  11. if not self.arr:
  12. return
  13. return -self.arr[0]

python3中的heapq模块使用的更多相关文章

  1. python接口自动化测试二十七:密码MD5加密 ''' MD5加密 ''' # 由于MD5模块在python3中被移除 # 在python3中使用hashlib模块进行md5操作 import hashlib # 待加密信息 str = 'asdas89799,.//plrmf' # 创建md5对象 hl = hashlib.md5() # Tips # 此处必须声明encode # 若写法为

    python接口自动化测试二十七:密码MD5加密   ''' MD5加密 '''# 由于MD5模块在python3中被移除# 在python3中使用hashlib模块进行md5操作import has ...

  2. python3 中mlpy模块安装 出现 failed with error code 1的决绝办法(其他模块也可用本方法)

    在python3 中安装其它模块时经常出现 failed with error code 1等状况,使的安装无法进行.而解决这个问题又非常麻烦. 接下来以mlpy为例,介绍一种解决此类安装问题的办法. ...

  3. 基于python3.x,使用Tornado中的torndb模块操作数据库

    目前Tornado中的torndb模块是不支持python3.x,所以需要修改部分torndb源码即可正常使用 1.开发环境介绍 操作系统:win8(64位),python版本:python3.6(3 ...

  4. 详解:Python2中的urllib、urllib2与Python3中的urllib以及第三方模块requests

    在python2中,urllib和urllib2都是接受URL请求的相关模块,但是提供了不同的功能.两个最显著的不同如下: 1.urllib2可以接受一个Request类的实例来设置URL请求的hea ...

  5. python 中的堆 (heapq 模块)应用:Merge K Sorted Lists

    堆是计算机科学中一类特殊的数据结构的统称.堆通常是一个可以被看做一棵树的数组对象.在队列中,调度程序反复提取队列中第一个作业并运行,因为实际情况中某些时间较短的任务将等待很长时间才能结束,或者某些不短 ...

  6. python之模块copy_reg(在python3中为copyreg,功能基本不变)

    # -*- coding: utf-8 -*-#python 27#xiaodeng#python之模块copy_reg(在python3中为copyreg,功能基本不变) import copy_r ...

  7. python之模块配置文件ConfigParser(在python3中变化较大)

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之模块ConfigParser(在python3中为configparser) #特别注意:py ...

  8. 把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败 在python3中调用会成功,但是调用不能成功的解决方案

    把模块有关联的放在一个文件夹中 在python2中调用文件夹名会直接失败在python3中调用会成功,但是调用不能成功 解决办法是: 在该文件夹下加入空文件__init__.py python2会把该 ...

  9. Python3中正则模块re.compile、re.match及re.search函数用法详解

    Python3中正则模块re.compile.re.match及re.search函数用法 re模块 re.compile.re.match. re.search 正则匹配的时候,第一个字符是 r,表 ...

随机推荐

  1. O046、掌握Cinder 的设计思想

    参考https://www.cnblogs.com/CloudMan6/p/5578673.html   从 volume  创建流程看 cinder-* 子服务如何协同工作   对于 Cinder  ...

  2. js中神奇的东西

    简单了解一些js的东西 window.history.go(-1);//历史记录-1,跳转到上一次操作的页面 Location 对象的 replace() 方法用于重新加载当前文档(页面) javas ...

  3. haproxy + keepalived + mycat 高可用与负载均衡集群配置 centos7

    架构如上,但是其实keepalived.haproxy.Mycat都可以多台(比如keepalived.haproxy.Mycat各3台,3台keepalived抢占vip,然后抢到vip的hapro ...

  4. H5移动端弹幕动画实现

    思路 把单个内容编辑好,计算自身宽度,确定初始位置 移动的距离是屏幕宽度 js动态的添加css动画函数,将高度.动画移动时间.动画延迟时间都用随机数控制 代码: html骨架结构 (以三个为例,如果觉 ...

  5. SpringBoot整合MongoDB JPA,测试MongoRepository与MongoTemplate用法,简单增删改查+高级聚合

    源码 地址 -> https://github.com/TaoPanfeng/case/tree/master/04-mongo/springboot-mongo 一 引入依赖 <depe ...

  6. linux重装docker-compose后无法执行docker-compose命令

    背景 使用自动化脚本重装docker和docker-compose(但脚本中未对旧版本的docker-compose进行任何处理,比如卸载删除) 导致执行docker-compose命令时报了错,大多 ...

  7. Linux下安装opencv with-ffmpeg解决无法读取视频的问题

    1. 编译安装ffmpeg 下载源码,执行 ./configure --disable-yasm --enbale-shared --prefix=/usr/local/ffmpeg 即可. 2. 下 ...

  8. XML和XML解析

    1. XML文件: 什么是XML?XML一般是指可扩展标记语言,标准通用标记语言的子集,是一种用于标记电子文件使其具有结构性的标记语言. 2.XML文件的优点: 1)XML文档内容和结构完全分离. 2 ...

  9. python常用模块:模块练习

    今日作业: 1.简述 什么是模块 模块就将一些函数功能封装在一个文件内,以‘文件名.py’命名,以“import 文件名”方式调用 模块有哪些来源  自定义.内置.DLL编译器.包模块的格式要求有哪些 ...

  10. java 如何读取src根目录下的属性文件

    在java项目中,如何获取src根目录下的属性文件/资源文件呢? 有如下三种方式: 方式一: InputStream in = Test.class .getResourceAsStream(&quo ...