python优先级排序_用Python实现优先级队列的3种方法
微信公众号:冰咖啡与狗
1. 什么是优先级队列?
优先级队列是一种容器型数据结构,它能管理一队记录,并按照排序字段(例如一个数字类型的权重值)为其排序。由于是排序的,所以在优先级队列中你可以快速获取到最大的和最小的值。
可以认为优先级队列是一种修改过的普通队列:普通队列依据记录插入的时间来获取下一个记录,而优先级队列依据优先级来获取下一个记录,优先级取决于排序字段的值。
优先级队列常用来解决调度问题,比如给紧急的任务更高的优先级。以操作系统的任务调度为例:高优先级的任务(比如实时游戏)应该先于低优先级的任务(比如后台下载软件更新)执行。
在 Python 中,内置的标准库提供了两种实现优先队列的数据结构,分别是 heapq 模块和 PriorityQueue 模块,接下来本文将对如何利用这两个数据结构实现优先级队列进行介绍,包括高优先级队列、低优先级队列,以及面向对象的优先级队列。这两种数据结构,其本质都是利用了堆的数据结构来实现优先队列,因此最后会利用列表来实现一个堆排序,然后在实现优先级队列。
先简单介绍 heap(堆)的概念。堆,是对于每一个父节点上的值都小于或等于子节点的值的二叉树。此外,一个堆必须是一个完整的二叉树,除了最底层,其他每一级必须是被完整填充的。因此,堆的最重要的一个特点就是:首项 heap[0] 总是最小的一项。
2. heapq 模块
heapq 是一个二叉堆的实现,其内部使用内置的 list 对象,对于列表中的每一个元素,其满足 a[k] <= a[2*k+1] and a[k] <= a[2*k+2] ,因此,该方法默认的是一个最小堆,a[0] 为队列中的最小元素。
2.1. 最小优先队列实现
由于 heapq 模块默认的就是最小堆结构,所以可以直接利用其中的函数操作来实现最小优先队列的功能。
def smallest_queue(arr):
"""默认为最小优先队列"""
print("原数组:{0}".format(arr))
# 将给定的列表转化为最小堆,线性时间
heapq.heapify(arr)
print("最小堆数组:{0}".format(arr))
# 插入元素
heapq.heappush(arr, 5)
print("插入新元素后:{0}".format(arr))
# 弹出最小元素
item0 = heapq.heappop(arr)
print("弹出的元素后:{0}".format(arr))
# 返回最小元素
item1 = arr[0]
print("获取最小元素的值:{0}".format(item1))
# 弹出最小元素,并插入一个新的元素,相当于先 heappop, 再 heappush
item2 = heapq.heapreplace(arr, -2)
print("弹出的元素为:{0}".format(item2))
print("现在的堆结构为:{0}".format(arr))
2.2. 最大优先队列的实现
除了最小堆以外,最大堆也是我们常用的一种数据结构,利用 heapq 模块可以实现最大堆的一些简单操作。
def largest_queue(arr):
"""最大优先队列的实现阅读 heapq 模块的源代码可以发现,其内置了最大优先队列的实现函数和操作函数,但没有内置新插入元素的函数"""
print("原数组:{0}".format(arr))
# 将给定的列表转化为最大堆,线性时间
heapq._heapify_max(arr)
print("最大堆数组:{0}".format(arr))
# 弹出最大元素
item0 = heapq._heappop_max(arr)
print("弹出的元素为:{0}".format(item0))
print("弹出的元素后:{0}".format(arr))
# 弹出最大元素,并插入一个新的元素
item1 = heapq._heapreplace_max(arr, 9)
print("弹出的元素为:{0}".format(item1))
print("现在的堆结构为:{0}".format(arr))
2.3. 复杂结构的优先队列
在现实场景中,我们在使用优先队列时往往不是针对简单的数组进行优先排列,而是对元组、列表或者字典等一个个复杂的对象进行优先排列。下面以元组为例,其在构建优先队列时,默认是以第一个元素来进行排列,当第一个元素相同时,则开始比较第二个。字符串的比较从首字母开始进行比较。
def object_queue():
"""针对对象结构的优先队列"""
q = []
heapq.heappush(q, (2, 'code'))
heapq.heappush(q, (1, 'eat'))
heapq.heappush(q, (3, 'sleep'))
heapq.heappush(q, (2, 'play'))
heapq.heappush(q, (3, "debug"))
q1 = [x for x in q]
while q:
next_item = heapq.heappop(q)
print(next_item)
# ---- result -----
# (1, 'eat')
# (2, 'code')
# (2, 'play')
# (3, 'debug')
# (3, 'sleep')
# 返回最小的 n 个元素,相当于 sorted(iterable, key=key)[:n]
n_smallest = heapq.nsmallest(3, q1, key=lambda x: x[0])
print("最小的3个元素:{0}".format(n_smallest))
# 返回最大的 n 个元素,相当于 sorted(iterable, key=key, reverse=True)[:n]
n_largest = heapq.nlargest(3, q1, key=lambda x: x[1])
print("最大的3个元素:{0}".format(n_largest))
3. PriorityQueue 模块
该模块定义的优先级队列,其内部使用了 heapq 模块,所以它的时间复杂度和heapq是相同的。
当一个对象的所有元素都是可比较的时,默认情况下是根据队列中的对象的第一个元素进行排序,越小的优先级越高,排在越前面。当第一个元素相同时,依次比较后续的元素的大小来进行排序。
由于 PriorityQueue 是继承自 Queue 类,所以很多函数的用法可以直接参照于 Queue 类中的函数。
from queue import PriorityQueue as PQ
pq = PQ()
pq.put((1, 'a'))
pq.put((2, 'c'))
pq.put((2, 'b'))
pq.put((2, 'b'))
print(pq.queue) # [(1, 'a'), (2, 'b'), (2, 'b'), (2, 'c')]
item0 = pq.get() # (1, 'a')
print(pq.queue) # [(2, 'b'), (2, 'b'), (2, 'c')]
print(pq.qsize()) # 优先队列的尺寸
while not pq.empty():
print(pq.get())与 heapq 模块不同的是,PriorityQueue 是基于类实现的,其提供的操作是同步的,提供锁操作,支持并发的生产者和消费者。
4. 实现自己的优先队列
在面向对象的编程过程中,我们通常是将一些单独的函数或变量组合成一个对象,然后在进行优先级排列。例如我们现在有很多种汽车,汽车有名字和价格,以及一些操作方法。当我们对汽车对象来按照价格进行优先级排列时,由于自定义的对象是不可比较的,所以在进行优先级排列时会报错。因此对于那些自定义的对象,我们需要重写优先级队列的方法来进行实现。
由于 PriorityQueue 也是基于 heapq 实现的,所以我们自定义的优先级队列可以直接基于 heapq 模块来实现。
import heapq
class My_PriorityQueue(object):
def __init__(self):
self._queue = []
self._index = 0
def push(self, item, priority):
"""队列由 (priority, index, item) 形式组成priority 增加 "-" 号是因为 heappush 默认是最小堆index 是为了当两个对象的优先级一致时,按照插入顺序排列"""
heapq.heappush(self._queue, (-priority, self._index, item))
self._index += 1
def pop(self):
"""弹出优先级最高的对象"""
return heapq.heappop(self._queue)[-1]
def qsize(self):
return len(self._queue)
def empty(self):
return True if not self._queue else False
class Car(object):
def __init__(self, name, value):
self.name = name
self.value = value
def __repr__(self):
return "{0}--{1}".format(self.name, self.value)
if __name__ == "__main__":
car1 = Car("BMW", 45)
car2 = Car("Maybach", 145)
car3 = Car("Bugatti", 85)
car4 = Car("Cadillac", 78)
car5 = Car("Maserati", 85)
pq = My_PriorityQueue()
pq.push(car1, car1.value)
pq.push(car2, car2.value)
pq.push(car3, car3.value)
pq.push(car4, car4.value)
pq.push(car5, car5.value)
print("队列大小:{0}".format(pq.qsize()))
# 弹出元素
while not pq.empty():
print(pq.pop())
5. 源码文件
python优先级排序_用Python实现优先级队列的3种方法相关推荐
- python dictionary排序_对Python的字典进行排序
我们知道Python的内置dictionary数据类型是无序的,通过key来获取对应的value.可是有时我们需要对dictionary中 的item进行排序输出,可能根据key,也可能根据value ...
- python 时间序列预测_使用Python进行动手时间序列预测
python 时间序列预测 Time series analysis is the endeavor of extracting meaningful summary and statistical ...
- python 概率分布模型_使用python的概率模型进行公司估值
python 概率分布模型 Note from Towards Data Science's editors: While we allow independent authors to publis ...
- python numpy读取数据_大神教你python 读取文件并把矩阵转成numpy的两种方法
导读 今天小编就为大家分享一篇python 读取文件并把矩阵转成numpy的两种方法,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 在当前目录下: 方法1: file = open( ...
- python大神读取_大神教你python 读取文件并把矩阵转成numpy的两种方法
导读 今天小编就为大家分享一篇python 读取文件并把矩阵转成numpy的两种方法,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧 在当前目录下: 方法1: file = open( ...
- Python进阶之使用Scrapy实现自动登录Github的两种方法(POST,FormRequest,from_response)
Python进阶之使用Scrapy实现自动登录Github的两种方法 1. 通过.FormRequest()实现登录github github1.py 2. 通过.FormRequest.from_r ...
- python中用def实现自动排序_用 python 实现各种排序算法
常见集中排序的算法 归并排序 归并排序也称合并排序,是分治法的典型应用.分治思想是将每个问题分解成个个小问题,将每个小问题解决,然后合并. 具体的归并排序就是,将一组无序数按n/2递归分解成只有一个元 ...
- python将对象放入列表根据某个属性排序_关于python:如何根据对象的属性对对象列表进行排序?...
我有一个python对象列表,我想按对象本身的属性排序.列表如下: >>> ut [, , , , , , ...] 每个对象都有一个计数: >>> ut[1].c ...
- python汉字排序_【IT专家】Python中文排序(转载)
本文由我司收集整编,推荐下载,如有疑问,请与我司联系 Python 中文排序(转载) 2012/02/02 3793 Python 比较字符串大小时,根据的是 ord 函数得到的编码 值.基于它的排序 ...
最新文章
- Android Java使用JavaMail API发送和接收邮件的代码示例
- 经济学与计算机科学结合,理论计算机在物理学和经济学领域的重要作用
- CSS3--幽灵按钮特效(实例)
- 停止Java线程,小心interrupt()方法
- 跟我一起学Redis之高可用从主从复制开始
- MongoDB Shell操作
- 京东壕掷27亿买下一座酒店 官方回应:以办公为主!
- 什么是平板电脑的杀手锏?
- mac vscode配置c++ debug环境
- 【蓝桥省赛倒计时】B组Java冲刺打卡(三)
- 安装Office InfoPath 2007
- Kotlin - 伴生对象与静态成员
- SpringBoot 优雅地对接口进行数据加解密
- vrchat新手教程_VRChat入门指南| 最新电脑资讯
- Hadoop MapReduce Job 相关参数设置 概念介绍与理解
- 2019年年终个人总结
- java 设计模式 路由器_Java设计模式——工厂模式
- win7出现无法连接到代理服务器的错误,不能上网的问题的解决
- vue-父组件向子组件共享数据
- 02.1 知识图谱工具 Protege的下载安装与使用