Python中的优先队列(priority queue)和堆(heap)

 更新时间:2022年09月28日 09:39:52   作者:笨笨的蛋  
这篇文章主要介绍了Python中的优先队列(priority queue)和堆(heap),具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

队列和优先队列(Priority Queue)

队列是一种可以完成插入和删除的数据结构。普通队列是先进先出(FIFO), 即先插入的先被删除。

然而在某些时候我们需要按照任务的优先级顺序来决定出队列的顺序,这个时候就需要用到优先级队列了。优先队列是一种可以完成插入和删除最小元素的数据结构

python中有现成的优先队列类可以调用

代码示例

from queue import Queue   # 队列,FIFO
from queue import PriorityQueue  #优先级队列,优先级高的先输出
###############队列(Queue)的使用,/python中也可是用列表(list)来实现队列###############
q = Queue(maxsize) 	#构建一个队列对象,maxsize初始化默认为零,此时队列无穷大
q.empty()		#判断队列是否为空(取数据之前要判断一下)
q.full()		#判断队列是否满了
q.put()			#向队列存放数据
q.get()			#从队列取数据
q.qsize()		#获取队列大小,可用于判断是否满/空
###用法示例:
>>> q = Queue(3)
>>> for i in range(3):
>>>	q.put(i)
>>> q.full()
True
>>> while not q.empty():
>>> 	print(q.get())
0
1
2
###############优先队列(PriorityQueue)的使用###############
"""
队列的变体,按优先级顺序(最低优先)检索打开的条目。
条目通常是以下格式的元组:
插入格式:q.put((priority number, data))
特点:priority number 越小,优先级越高
其他的操作和队列相同
"""
>>> q = PriorityQueue()
>>> q.put((2, "Lisa"))
>>> q.put((1, "Lucy"))
>>> q.put((0, "Tom"))
>>> i = 0
>>> while i < q.qsize():
>>> 	print(q.get())
(0,"Tom")
(1,"Lucy")
(2,"Lisa")
#可见并没有按照输入的顺序输出的,而是按照优先级顺序输出的
#优先独队列的实质是

堆(heap)

看完上面的示例我们也许会有疑惑,优先队列是如何来实现的了?

----------优先队列的实质是每次插入时按照一定的规则插入,删除时直接找到最小的那个元素弹出即可。插入和弹出最坏情况下的时间复杂度都是O(LogN)

下面我们就来介绍实现优先队列的一种数据结构——堆(heap)

简介

堆的实质是一个完全二叉树(除最底层外,其余层都是满二叉树,且最底层的节点从左往右顺序排列)。 堆主要分为大顶堆小顶堆两种

1.大顶堆:每个分支的根节点都大于等于其子节点。(ki >= k2i)and (ki >= k2i+1)

2.小顶堆:每个分支的根节点都小于等于其子节点。(ki <= k2i)and (ki <= k2i+1)

所以堆中的第i个元素的父亲是floor(i/2)(向下取整), 第i个元素的左右孩子分别是2i2i+1

堆的根节点要从1开始计算,不能从0开始计算。(假如从零开始i=2时其父亲节点为floor(2/2)=1, 显然是错误的)

初始化构建堆

将一个有k个元素的数组构成一个大顶堆或者小顶堆。时间复杂度O(n)$

核心思路:从下往上,从右往左,且每次都从第floor(k/2)个元素开始进行调整依次递减直到根节点

1.从floor(k/2)个元素开始进行调整,调整完毕,则i--;
    #调整节点,每次只调整该根节点以下的子树
    while i <= k
        1.1 找出左右孩子中最大的那个
        1.2 该节点和较大的那个子节点比较,
                如果父节点较大,break
                如果子节点较大,记录子节点的索引,把子节点的值给父节点
        1.3 将上面较大的那个字节点作为新的父节点开始一轮循环
    循环结束后,将父节点的值放到他应该去的子节点的位置上

例如有如下数组

把其转化成完全二叉树的形式就是

该数组一共有9个元素,所以从i = floor(9/2)=4 个元素开始,第4个元素是6, 其右孩子9最大,将6和9交换完成一次调整.

i=3,第三个元素是8,8比左右孩子都大,不用交换

i=2,此时的子树是:第二个元素是2,而此时以2为根节点的子树如下左图。交换过程为: a).2与9交换,此时的根节点就到了2现在的位置 (下中图)。b).2与7交换。 c)交换完成,本次调整完毕

i=1,此时的父节点就是整棵树的根节点,而此时整棵树的状态是下图所示

a). 5和9交换,此时5到了9的位置(下左图) b). 5和7交换,5到了7的位置(下中图) c).最后5和6交换,完成调整。


经过以上的floor(k/2)次的调整之后,由一个无序数组初始化构建大顶堆就完成了

堆的插入(节点上浮)

时间复杂度O(log(k+1))

我们之前由一个无序数组构造了一个大顶堆,那么现在我们要插入一个新的元素该怎么办了,如下图,如果直接在最末尾插入,则破坏了堆的结构,所以还需要按进行调整。

调整规则:由于其他子树的顺序都已经调整完毕,所以只需要调整根节点到插入节点所在的子路即可。

如下图所示。(a)将11与4调整,(b)将11与7调整,©再将11与9调整。最后得到调整完毕的结果。

堆的删除(节点下浮)

时间复杂度O(log(k-1))

由于堆是队列结构,只能从堆中删除堆顶的元素。移除堆顶元素之后,之前的完全二叉树就变成来两个子树,次数最方便的做法就是用堆的最后一个节点来填补取走的堆顶元素,并将堆的实际元素个数减1。但是这样做以后又不满足堆的定义了,还是要进行调整。

调整规则:从根节点开始逐层向下调整即可。


(a).将5与8调换,(b)调整完成,删除完毕

堆的应用

之间讲过的利用堆(python中是小顶堆)来实现优先队列

利用堆求Top k:

假如现在有1亿个数据要求其中前十个最小的元素。最笨方法就是对所有数据进行排序,然后找出前十个最小的,即使使用快排,也需要O(NlogN)的时间复杂度。假如利用大顶堆的话–先利用前10个数据构造一个大顶堆,然后顺序遍历数组与大顶堆顶点比较,假如比大于等于顶点直接删除,否则就删除堆顶点并插入新的节点,最后堆里面的元素就是最小的前是个元素。这样求出来的时间复杂度为Nlog(k)

假如要求前十个最大的元素,那就要使用小顶堆来实现

最后放上python手动实现将数组转化为大顶堆和小顶堆的代码

class Heap:
	def __init__(self, _list):
		self._list = _list
		self.k = len(_list)
		
	def _bigHeapAdjuest(self, j):
		temp = self._list[j] #存放当前双亲节点的值,j代表当前节点的位置
		i = 2*j  			 #j的左孩子
		while i <= self.k:
			if i < self.k and self._list[i] < self._list[i+1]:  #i==n时由于只有一个子节点,所以没有比较的必要
				i += 1
			#与双亲比较,如果双亲大则跳过
			#如果双亲小,就把大的给双亲
			if temp >= self._list[i]:
				break                 			#没有必要修整
			self._list[j] = self._list[i]       #元素交换
			j = i                               #j记录着temp应该去的位置,由于这个位置的节点发生了变动,所以需要再次调整
			i *= 2             					#接着找该节点的左孩子
		self._list[j] = temp	
	
    def _smallheapAdjuest(self, j):
        temp = self._list[i] #父节点
        i = 2*j  #左孩子
        while i <= self.k:
            if i < self.k and self._list[i] > self._list[i+1]:    #找最小的那个孩子
                i += 1
            #构建小顶堆,小于父节点的都要上浮
            if self._list[i] >= temp:
                break
            self._list[j] = self._list[i]  #子节点交给父节点
            j= i    #记录父节点应该去的索引位置
            i *= 2
        self._list[j] = temp
	
	def heapSort(self):
		self._list.insert(0, -1) 
		s = self.length // 2  
		#构建大顶堆
		for j in range(s, 0, -1):
			self._bigHeapAdjuest(j) 
		#构建小顶堆
			for j in range(s, 0, -1):
			self._SmallheapAdjuest(j) 
		

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Python 处理数据的实例详解

    Python 处理数据的实例详解

    这篇文章主要介绍了Python 处理数据的实例详解的相关资料,这里主要介绍Python 常用的基础知识并附实例,需要的朋友可以参考下
    2017-08-08
  • Pytest 自动化测试框架的使用

    Pytest 自动化测试框架的使用

    本文主要介绍了Pytest 自动化测试框架的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-03-03
  • pytorch逐元素比较tensor大小实例

    pytorch逐元素比较tensor大小实例

    今天小编就为大家分享一篇pytorch逐元素比较tensor大小实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-01-01
  • Python+Qt相片更换背景颜色窗体程序的步骤详解

    Python+Qt相片更换背景颜色窗体程序的步骤详解

    QT+Python是非常经典的窗体编程组合,功能完善,可视化界面美观易维护,这篇博客针对相片更换背景颜色方面编写代码,代码整洁,规则,易读,对学习与使用Python有较好的帮助,需要的朋友可以参考下
    2022-12-12
  • python使用matplotlib绘制雷达图

    python使用matplotlib绘制雷达图

    这篇文章主要为大家详细介绍了python使用matplotlib绘制雷达图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • Python黑魔法@property装饰器的使用技巧解析

    Python黑魔法@property装饰器的使用技巧解析

    @property装饰器能把一个方法变成属性一样来调用,下面我们就一起来看看Python黑魔法@property装饰器的使用技巧解析
    2016-06-06
  • Python实现考试自动答题的脚本分享

    Python实现考试自动答题的脚本分享

    最近这段时间天气正正好,不冷不热,是学习考驾照的好时机。为了帮助大家能够顺利获得驾照,小编为大家准备了驾照考试的自动答题小程序,希望对大家有所帮助
    2023-03-03
  • Tkinter组件Entry的具体使用

    Tkinter组件Entry的具体使用

    本文主要介绍了Tkinter组件Entry的具体使用,Entry组件通常用于获取用户的输入文本,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • python 读取txt中每行数据,并且保存到excel中的实例

    python 读取txt中每行数据,并且保存到excel中的实例

    下面小编就为大家分享一篇python 读取txt中每行数据,并且保存到excel中的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-04-04
  • Python SMTP配置参数并发送邮件

    Python SMTP配置参数并发送邮件

    这篇文章主要介绍了Python SMTP配置参数并发送邮件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-06-06

最新评论