优先级队列

如果我们给每个元素都分配一个数字来标记其优先级,不妨设较小的数字具有较高的优先级,这样我们就可以在一个集合中访问优先级最高的元素并对其进行查找和删除操作了。这样,我们就引入了优先级队列 这种数据结构

最简单的优先级队列可能就是一堆不同大小的数组成的队列,每次需要取出其中最小或最大的数,这是我们可以把这些数本身的大小叫做他们的优先级。

实现的想法

最简单的想法是:我们用一个元组来表示元素和它的优先级,将所有的元组都放到列表中存储,接下来当想要找到其中优先级最小的元组时会有以下两种方式

  • 1.列表中存储的是乱序的,每次想找最小的时候就遍历一遍找到优先级最小的元组取出。这样操作的时间复杂度其实是O(n)

  • 2.列表中的元素已经按照优先级的顺序排好了序,每次取最小的元素时直接找固定位置,但是每次向该优先级队列插入元素时都要进行一次排序将其放入合适的位置,在最坏情况下,时间复杂度同样为O(n)

上面这两种方式的时间复杂度还是较高的,为此,我们使用一种叫做堆的结构来实现优先级队列。

堆其实是一颗二叉树,但它是一种特殊的二叉树,堆中的每个父节点都是要大于等于或者小于等于它的孩子节点。这里是选择大于等于还是小于等于是自己定义,但树中的所以节点都满足一条同样的规则。

在使用堆时,一般我们会想让堆的高度尽可能的小,为此它必须是一颗完全二叉树,这时就会产生一个结论:

堆中有n个元素,那么它的高度h=[log(n)]

我们证明一下这个结论:

完全二叉树0~h-1层的节点数目为2^h-1,在第h层,节点数目最少为1,最多为2^h

于是,n>=2^h-1+1且n<=2^h-1+2^h

分别两边取对数,得出h<=log(n)和h>=log(n+1)-1

由于h是整数,所以h=[log(n)]

用堆实现优先级队列

插入元素

插入元素包括向堆中添加一个元素和堆向上冒泡

添加元素时要为了满足 完全二叉树的特性,需要将其放到树最下层的最右节点的最右位置,如果最下层已经满了,则放到再下一层的最左位置。

堆向上冒泡是一个很有趣的算法,为了使添加元素后的树满足堆排序,需要做一定的调整,调整方法为将添加的元素的优先级与其父节点相比较,如果小于父节点,则该元素与父节点交换,然后再与新的父节点比较,知道父节点小于了自己的优先级或者自己成为了根节点。如图:

上面的树调整了之后变成了下面的树

移除最小元素

移除最小元素,按理说最小元素就是二叉树的根节点,但是将根节点删除之后,就变成了两颗分离的树,为了保持二叉树的完整性,我们要进行如下操作

首先将根节点与最下层的最右端的节点交换,然后删除这最下层最右端的节点,然后再进行堆的向下排序

堆的向下排序即为将根节点与两个孩子中最小的比较,如果该节点比孩子节点大,则与孩子节点交换,然后继续向下进行直到该节点比两个孩子节点都小或者该节点已经没有孩子了为止。如图:

堆的插入与移除元素的复杂度都是log(n)

python实现

对于二叉树的实现,这次我们不使用链式结构,因为堆的排序中,需要定位最下层最右端的这个节点,链式实现起来较为复杂,同时堆是完全二叉树,所以使用基于列表的方式会使问题方便很多

先介绍一下这种实现方式:

列表的首个元素即为二叉树的根节点,所以根节点的索引为1

设节点p的索引函数为f(p)

如果p是位置q的左孩子,则f(p) = 2f(q)+1

如果p是位置q的右孩子,则f(p) = 2f(q)+2

列表中的最后一个元素就是二叉树的最下层的最右端的元素

下面是具体代码:


class Empty(Exception):
pass class HeapPriorityQueue(): """
使用堆与列表实现的优先级队列
""" class Item():
"""
队列中的项类
""" def __init__(self, key, value):
self.key = key
self.value = value def __it__(self, other):
return self.key < other.key def is_empty(self):
return len(self) == 0 def parent(self, j):
"""
返回父节点的索引
"""
return (j - 1) // 2 def left(self, j):
"""返回左孩子索引"""
return 2 * j + 1 def right(self, j):
"""返回右孩子索引"""
return 2 * j + 2 def has_left(self, j):
"""通过判断索引是否出了列表来判断是否存在"""
return self.left(j) < len(self.data) def has_right(self, j):
return self.right(j) < len(self.data) def swap(self, i, j):
self.data[i], self.data[j] = self.data[j], self.data[i] def upheap(self, j):
"""向上堆排序"""
parent = self.parent(j)
if j > 0 and self.data[j] < self.data[parent]:
self.swap(j, parent)
self.upheap(parent) def downheap(self, j):
"""向下堆排序"""
if self.has_left(j):
left = self.left(j)
small = left
if self.has_right(j):
right = self.right(j)
if self.data[right] < self.data[left]:
small = right
if self.data[small] < self.data[j]:
self.swap(small, j)
self.downheap(small) def __init__(self):
self.data = [] def __len__(self):
return len(self.data) def add(self, key, value):
"""添加一个元素,并进行向上堆排序"""
self.data.append(self.Item(key, value))
self.upheap(len(self.data) - 1) def min(self):
if self.is_empty():
raise Empty('Priority queue is empty')
item = self.data[0]
return (item.key, item.value) def remove_min(self):
if self.is_empty():
raise Empty('Priority queue is empty')
self.swap(0, len(self.data) - 1)
item = self.data.pop()
self.downheap(0)
return (item.key, item.value)

python的heapq模块

Python标准包含了heapq模块,但他并不是一个独立的数据结构,而是提供了一些函数,这些函数吧列表当做堆进行管理,而且元素的优先级就是列表中的元素本身,除此之外它的模型与实现方式与刚才我们自己定义的基本相同

有以下函数:

  • heappush(L,e): 将元素e存入列表L中并进行堆排序

  • heappop(L): 取出并返回优先级最小的元素,并重新堆排序

  • heappushpop(L,e): 将e放入列表中,同时返回最小元素,相当于先执行push,再pop

  • heap replace(L,e): 与heappushpop类似,是先执行pop,再执行push

  • heapify(L): 将未堆排序的列表进行调整使之满足堆的结构。采用了自底向上的堆构造算法,时间复杂度为O(n)

  • 等等

用Python实现数据结构之优先级队列的更多相关文章

  1. Python之实现一个优先级队列

    问题 怎样实现一个按优先级排序的队列? 并且在这个队列上面每次 pop 操作总是返回优先级最高的那个元素 解决方案 下面的类利用 heapq 模块实现了一个简单的优先级队列: import heapq ...

  2. 优先级队列Priority_queue

    定义 拥有权值观点的queue,,一个是返回最高优先级对象,一个是在底端添加新的对象.这种数据结构就是优先级队列(Priority Queue) . 实现 利用max_heap完成,以vector表现 ...

  3. [PY3]——实现一个优先级队列

    import heapq class PriorityQueue: def __init__(self): self._queue=[] self._index=0 def push(self,ite ...

  4. 【python cookbook】【数据结构与算法】5.实现优先级队列

    问题:要实现一个队列,它能够以给定的优先级对元素排序,且每次pop操作时都会返回优先级最高的那个元素: 解决方案:采用heapq模块实现一个简单的优先级队列 # example.py # # Exam ...

  5. 【数据结构与算法Python版学习笔记】树——利用二叉堆实现优先级队列

    概念 队列有一个重要的变体,叫作优先级队列. 和队列一样,优先级队列从头部移除元素,不过元素的逻辑顺序是由优先级决定的. 优先级最高的元素在最前,优先级最低的元素在最后. 实现优先级队列的经典方法是使 ...

  6. java数据结构与算法值优先级队列

    一.优先级队列 什么是优先级队列:优先级队列是一种比栈和队列更加常用的一种数据结构.在优先级队列中,数据项按照关键字的值有序.数据项插入到队列中时,会按照顺序插入到合适的位置,用来保证队列的顺序. 生 ...

  7. 自己动手实现java数据结构(八) 优先级队列

    1.优先级队列介绍 1.1 优先级队列 有时在调度任务时,我们会想要先处理优先级更高的任务.例如,对于同一个柜台,在决定队列中下一个服务的用户时,总是倾向于优先服务VIP用户,而让普通用户等待,即使普 ...

  8. Python线程优先级队列(Queue)

    Python的Queue模块中提供了同步的.线程安全的队列类,包括FIFO(先入先出)队列Queue,LIFO(后入先出)队列 LifoQueue,和优先级队列PriorityQueue.这些队列都实 ...

  9. java数据结构----队列,优先级队列

    1.队列:和栈中的情况不同,队列中的数据项不总是从数组下标0开始,移除一个数据项后,队头指针会指向下标较高的数据项,其特点:先入先出 2.图解 3.队列的实现代码: 3.1.Queue.java pa ...

随机推荐

  1. .net core Jenkins持续集成Linux、Docker、K8S

    jenkins插件 系统管理 -> 管理插件,安装如下插件. #如果搜索不到去掉Plugin在搜索 GitLab Plugin Gitlab Hook Plugin #使用Gitlab账号做用户 ...

  2. String,StringBuffer与StringBuilder的理解

    String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全) 简 要的说, String 类型和 StringBuffer 类型的主要性 ...

  3. 移动端自动化测试-AppiumApi接口详解

    Appium 初始化配置信息(Desired Capabilities),Desired Capabilities实际上就是一个字典,它主要用于向Appium Server提供初始化配置参数,如:想要 ...

  4. XSS漏洞扫描工具:BruteXSS

    下载Brute,一个xss漏洞扫描工具:https://codeload.github.com/shawarkhanethicalhacker/BruteXSS/legacy.zip/master 我 ...

  5. PowerDesigner版本控制器设置权限

    PowerDesigner版本控制权限之前一直在Groups里面设置,一直没有效果,原因终于找出来了,PowerDesigner是要对每个库单独赋权限的,步骤如下: 连接上版本控制,在左侧菜单 Obj ...

  6. JavaWeb学习 (二十五)————监听器(Listener)

    一.监听器介绍 1.1.监听器的概念

  7. Spring-IOC实现【01-XML配置方式】

    IOC概念 IoC控制反转(IoC,Inversion of Control), 是一个概念,是一种思想.控制反转就 是对对象控制权的转移,从程序代码本身反转到了外部容器.把对象的创建.初始化. 销毁 ...

  8. sealed关键字

    1. sealed关键字    当对一个类应用 sealed 修饰符时,此修饰符会阻止其他类从该类继承.类似于Java中final关键字.    在下面的示例中,类 B 从类 A 继承,但是任何类都不 ...

  9. MVC架构介绍—查询功能的开发

    select和from语句 注意:select和from可以不设置,默认情况下: select获取映射表的所有字段: from获取实体映射表的表名:如果设置select则必须设置frorm,但是允许仅 ...

  10. Matlab Gauss quadrature

    % matlab script to demonstrate use of Gauss quadrature clear all close all % first derive the 2-poin ...