Python实现优先级队列结构的方法详解


Posted in Python onJune 02, 2016

最简单的实现
一个队列至少满足2个方法,put和get.
借助最小堆来实现.
这里按"值越大优先级越高"的顺序.

#coding=utf-8 
from heapq import heappush, heappop 
class PriorityQueue: 
  def __init__(self): 
    self._queue = [] 
 
  def put(self, item, priority): 
    heappush(self._queue, (-priority, item)) 
 
  def get(self): 
    return heappop(self._queue)[-1] 
 
q = PriorityQueue() 
q.put('world', 1) 
q.put('hello', 2) 
print q.get() 
print q.get()

 使用heapq模块来实现
下面的类利用 heapq 模块实现了一个简单的优先级队列:

import heapq

class PriorityQueue:
  def __init__(self):
    self._queue = []
    self._index = 0

  def push(self, item, priority):
    heapq.heappush(self._queue, (-priority, self._index, item))
    self._index += 1

  def pop(self):
    return heapq.heappop(self._queue)[-1]

下面是它的使用方式:

>>> class Item:
...   def __init__(self, name):
...     self.name = name
...   def __repr__(self):
...     return 'Item({!r})'.format(self.name)
...
>>> q = PriorityQueue()
>>> q.push(Item('foo'), 1)
>>> q.push(Item('bar'), 5)
>>> q.push(Item('spam'), 4)
>>> q.push(Item('grok'), 1)
>>> q.pop()
Item('bar')
>>> q.pop()
Item('spam')
>>> q.pop()
Item('foo')
>>> q.pop()
Item('grok')
>>>

仔细观察可以发现,第一个 pop() 操作返回优先级最高的元素。 另外注意到如果两个有着相同优先级的元素( foo 和 grok ),pop操作按照它们被插入到队列的顺序返回的。

 函数 heapq.heappush() 和 heapq.heappop() 分别在队列 _queue 上插入和删除第一个元素, 并且队列_queue保证第一个元素拥有最小优先级(1.4节已经讨论过这个问题)。 heappop() 函数总是返回”最小的”的元素,这就是保证队列pop操作返回正确元素的关键。 另外,由于push和pop操作时间复杂度为O(log N),其中N是堆的大小,因此就算是N很大的时候它们运行速度也依旧很快。

在上面代码中,队列包含了一个 (-priority, index, item) 的元组。 优先级为负数的目的是使得元素按照优先级从高到低排序。 这个跟普通的按优先级从低到高排序的堆排序恰巧相反。

index 变量的作用是保证同等优先级元素的正确排序。 通过保存一个不断增加的 index 下标变量,可以确保元素按照它们插入的顺序排序。 而且, index 变量也在相同优先级元素比较的时候起到重要作用。

为了阐明这些,先假定Item实例是不支持排序的:

>>> a = Item('foo')
>>> b = Item('bar')
>>> a < b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

如果你使用元组 (priority, item) ,只要两个元素的优先级不同就能比较。 但是如果两个元素优先级一样的话,那么比较操作就会跟之前一样出错:

>>> a = (1, Item('foo'))
>>> b = (5, Item('bar'))
>>> a < b
True
>>> c = (1, Item('grok'))
>>> a < c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

通过引入另外的 index 变量组成三元组 (priority, index, item) ,就能很好的避免上面的错误, 因为不可能有两个元素有相同的 index 值。Python在做元组比较时候,如果前面的比较以及可以确定结果了, 后面的比较操作就不会发生了:

>>> a = (1, 0, Item('foo'))
>>> b = (5, 1, Item('bar'))
>>> c = (1, 2, Item('grok'))
>>> a < b
True
>>> a < c
True
>>>

如果你想在多个线程中使用同一个队列,那么你需要增加适当的锁和信号量机制。 可以查看12.3小节的例子演示是怎样做的。

深入思考
函数 heapq.heappush() 和 heapq.heappop() 分别在队列 _queue 上插入和删除第一个元素, 并且队列_queue保证第一个元素拥有最小优先级(1.4节已经讨论过这个问题)。 heappop() 函数总是返回”最小的”的元素,这就是保证队列pop操作返回正确元素的关键。 另外,由于push和pop操作时间复杂度为O(log N),其中N是堆的大小,因此就算是N很大的时候它们运行速度也依旧很快。

在上面代码中,队列包含了一个 (-priority, index, item) 的元组。 优先级为负数的目的是使得元素按照优先级从高到低排序。 这个跟普通的按优先级从低到高排序的堆排序恰巧相反。

index 变量的作用是保证同等优先级元素的正确排序。 通过保存一个不断增加的 index 下标变量,可以确保元素按照它们插入的顺序排序。 而且, index 变量也在相同优先级元素比较的时候起到重要作用。

为了阐明这些,先假定Item实例是不支持排序的:

>>> a = Item('foo')
>>> b = Item('bar')
>>> a < b
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

如果你使用元组 (priority, item) ,只要两个元素的优先级不同就能比较。 但是如果两个元素优先级一样的话,那么比较操作就会跟之前一样出错:

>>> a = (1, Item('foo'))
>>> b = (5, Item('bar'))
>>> a < b
True
>>> c = (1, Item('grok'))
>>> a < c
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: Item() < Item()
>>>

通过引入另外的 index 变量组成三元组 (priority, index, item) ,就能很好的避免上面的错误, 因为不可能有两个元素有相同的 index 值。Python在做元组比较时候,如果前面的比较以及可以确定结果了, 后面的比较操作就不会发生了:

>>> a = (1, 0, Item('foo'))
>>> b = (5, 1, Item('bar'))
>>> c = (1, 2, Item('grok'))
>>> a < b
True
>>> a < c
True
>>>

如果你想在多个线程中使用同一个队列,那么你需要增加适当的锁和信号量机制。 可以查看12.3小节的例子演示是怎样做的。

heapq 模块的官方文档有更详细的例子程序以及对于堆理论及其实现的详细说明。

Python 相关文章推荐
Python时间戳与时间字符串互相转换实例代码
Nov 28 Python
基于Python实现的百度贴吧网络爬虫实例
Apr 17 Python
python使用线程封装的一个简单定时器类实例
May 16 Python
python书籍信息爬虫实例
Mar 19 Python
Python使用add_subplot与subplot画子图操作示例
Jun 01 Python
利用Python如何批量更新服务器文件
Jul 29 Python
python如何解析配置文件并应用到项目中
Jun 27 Python
Python Numpy,mask图像的生成详解
Feb 19 Python
Tensorflow实现将标签变为one-hot形式
May 22 Python
简单的命令查看安装的python版本号
Aug 28 Python
python中time、datetime模块的使用
Dec 14 Python
pytorch中[..., 0]的用法说明
May 20 Python
KMP算法精解及其Python版的代码示例
Jun 01 #Python
Python缩进和冒号详解
Jun 01 #Python
Python注释详解
Jun 01 #Python
深入理解python try异常处理机制
Jun 01 #Python
python学习 流程控制语句详解
Jun 01 #Python
python+Django+apache的配置方法详解
Jun 01 #Python
python中函数默认值使用注意点详解
Jun 01 #Python
You might like
php面向对象的方法重载两种版本比较
2008/09/08 PHP
PHP用星号隐藏部份用户名、身份证、IP、手机号等实例
2014/04/08 PHP
yii实现CheckBox复选框在同一行显示的方法
2014/12/03 PHP
PHP实现的随机IP函数【国内IP段】
2016/07/20 PHP
PHP自定义序列化接口Serializable用法分析
2017/12/29 PHP
PHP实现浏览器中直接输出图片的方法示例
2018/03/14 PHP
php项目中类的自动加载实例讲解
2019/09/12 PHP
PHP哈希表实现算法原理解析
2020/12/11 PHP
js select常用操作控制代码
2010/03/16 Javascript
JQuery对id中含有特殊字符的转义处理示例
2013/09/06 Javascript
form表单只提交数据而不进行页面跳转的解决方案
2013/09/18 Javascript
动态的创建一个元素createElement及删除一个元素
2014/01/24 Javascript
EasyUI实现第二层弹出框的方法
2015/03/01 Javascript
理解JavaScript中worker事件api
2015/12/25 Javascript
JavaScript学习笔记整理_用于模式匹配的String方法
2016/09/19 Javascript
ES6中Symbol类型用法实例详解
2017/04/06 Javascript
JS中的JSON对象的定义和取值实现代码
2018/05/09 Javascript
在Python中用has_key()方法查找键是否存在的教程
2015/05/21 Python
Python 操作文件的基本方法总结
2017/08/10 Python
使用Django连接Mysql数据库步骤
2019/01/15 Python
使用Python将Mysql的查询数据导出到文件的方法
2019/02/25 Python
Django认证系统实现的web页面实现代码
2019/08/12 Python
Django自定义YamlField实现过程解析
2020/11/11 Python
css3实现信纸/同学录效果的示例代码
2018/12/11 HTML / CSS
CSS3 clip-path 用法介绍详解
2018/03/01 HTML / CSS
安康杯竞赛活动总结
2014/05/05 职场文书
莫言诺贝尔获奖演讲稿
2014/05/21 职场文书
迎国庆演讲稿
2014/09/05 职场文书
学习朴航瑛老师爱岗敬业先进事迹思想汇报
2014/09/17 职场文书
统计学教授推荐信
2014/09/18 职场文书
工作态度怎么写
2015/06/25 职场文书
公司开业的祝贺语大全(60条)
2019/07/05 职场文书
Django cookie和session的应用场景及如何使用
2021/04/29 Python
如何利用Python实现一个论文降重工具
2021/07/09 Python
Python pyecharts绘制条形图详解
2022/04/02 Python
详解CSS3浏览器兼容
2022/12/24 HTML / CSS