保证一周更两篇吧,以此来督促自己好好的学习!代码的很多地方我都给予了详细的解释,帮助理解。好了,干就完了~加油!
声明:本python数据结构与算法是imooc上liuyubobobo老师java数据结构的python改写,并添加了一些自己的理解和新的东西,liuyubobobo老师真的是一位很棒的老师!超级喜欢他~
如有错误,还请小伙伴们不吝指出,一起学习~
No fears, No distractions.
一、优先队列
-
什么是优先队列?
普通队列:先进先出;后进后出
优先队列:出队顺序和入队顺序无关;和优先级相关。比如:看病的时候急诊患者和普通患者、操作系统中任务的调度。 -
优先队列本质上还是一个队列,所以接口:
# 为了更好的阐明意义,用C++来写的函数声明
void enqueue(E) # E入队
E dequeue() # 出队并获取出队元素
E getFront() # 获取队首元素
int getSize() # 获取优先队列的大小
bool isEmpty() # 判断优先队列是否为空
和普通队列的接口是一样的,只不过出队元素是谁是有讲究的,因为应该先出优先级最高的元素~
-
可能用到的底层数据结构分析:
如果用普通的线性结构来实现优先队列,入队时间复杂度:O(1),然而出队需要对队列进行一次扫描来拿到优先级最高的元素,此时时间复杂度为:O(n)
如果用排好序的线性结构来实现优先队列(比如优先级最高的元素始终位数组尾部),那么入队的时候就需要找到适当的位置,此时时间复杂度为O(n),相应的出队时间复杂度为O(1)
所以用以上两种数据结构来实现优先队列都会有O(n)的操作产生,这显然是不理想的,所以就引出了本节所要用到的底层结构——最大堆
堆的出队与入队操作的时间复杂度都是O(logn)的,性能非常的好。 -
只要你想要操作的元素支持比较操作(>,<),就能够放到优先队列中,本节测试用例都是用的正整数作为优先队列中的元素。。。
二、基于最大堆以及数组的优先队列的实现
# -*- coding: utf-8 -*-
# Author: Annihilation7
# Data: 2018-10-21 2:20 am
# Python version: 3.6
from Priorityqueue.maxheap import MaxHeap # 从maxheap.py文件中导入我们上一节实现MaxHeap类
from array.Array import Arr # 导入我们以前实现的数组
class PriorityQueue:
def __init__(self, initial_capacity=20):
"""
Description: 优先队列的构造函数
Params:
initial_capacity: 初始容量,由于底层是我们实现的数组类,有自动扩容,所以后面的操作不用过多的考虑底层。
"""
self._data = MaxHeap(initial_capacity) # 初始化我们的最大堆
def isEmpty(self):
"""
Description: 判空
Returns:
bool值,空为True
"""
return self._data.isEmpty() # 直接调用maxHeap的isEmpty就好了
def getSize(self):
"""
Description: 获取当前二叉堆中有效元素的个数
Returns:
有效元素的个数
"""
return self._data.getSize() # 直接调用maxHeap的getSize
def enqueue(self, elem):
"""
Description: 将元素elem入队
时间复杂度:O(logn)
Params:
- elem: 待入队元素
"""
self._data.add(elem) # 调用MaxHeap的add函数就完事了
def getFront(self):
"""
Description: 看一眼队首的元素是谁,也就是优先级最高的元素
时间复杂度:O(1)
Returns:
队首的元素
"""
return self._data.findMax() # 堆顶的元素就是优先级最高的元素呀,所以调用MaxHeap的findMax就好了
def dequeue(self):
"""
Description: 将队首元素出队,并将其返回
时间复杂度:O(logn)
Returns:
出队元素的值
"""
return self._data.extractMax() # 直接调用MaxHeap的extractMax,函数里面有检查了,就不需要判空了
class ArrayPriorityQueue:
"""基于数组的优先队列的实现,纯粹是为了对比用才实现的,主要还是要掌握基于最大堆的优先队列,这个看看就好"""
def __init__(self):
"""基于我们以前实现的数组的优先队列的构造函数"""
self._data = Arr()
def isEmpty(self):
"""
Description: 判空
Returns:
bool值,空为True
"""
return self._data.isEmpty() # 调用数组的isEmpty函数
def getSize(self):
"""
Description: 获取当前二叉堆中有效元素的个数
Returns:
有效元素的个数
"""
return self._data.getSize() # 调用数组的getSize函数
def enqueue(self, elem):
"""
Description: 将元素elem入队
时间复杂度:O(1)
Params:
- elem: 待入队元素
"""
self._data.addLast(elem) # 直接插入到队尾,此时复杂度为O(1)
def getFront(self):
"""
Description: 看一眼队首的元素是谁,也就是优先级最高的元素
时间复杂度:O(n)
Returns:
队首的元素
"""
return self._data[self._data.get_Max_index()] # 先获取最大值索引,再得到元素值即可
def dequeue(self):
"""
Description: 将队首元素出队,并将其返回
时间复杂度:O(n)
Returns:
出队元素的值
"""
return self._data.removeMax() # 直接调用数组的removeMax函数就好
三、性能对比测试
from queue.testQueueAndLoopqueue import count_time # 导入计算时间损耗的装饰器
from Priorityqueue.priorityqueue import PriorityQueue, ArrayPriorityQueue # 导入PriorityQueue和ArrayPriorityQueue
import numpy as np
np.random.seed(7)
nums = 10000 # 操作数目为10000次
max_heap_PriorityQueue = PriorityQueue() # 基于最大堆的优先队列的对象
arr_PriorityQueue = ArrayPriorityQueue() # 基于数组的优先队列的对象
record_list1 = []
record_list2 = []
def check_list(alist, flag):
"""
Description: 检查输入数组是否是严格的降序排列
Params:
- alist: 输入的list
- flag: bool值,判断测试的是基于最大堆的优先队列还是基于数组的优先队列
"""
for i in range(1, len(alist) - 1):
if alist[i] > alist[i - 1]:
raise Exception('Error! The list is not absolutely a reversed list!')
if flag:
print('Priority Queue which is based on the MaxHeap test completed.') # True为测试的基于最大堆的优先队列
else:
print('Priority Queue which is based on the Array test completed.') # False为测试的基于数组的优先队列
@count_time
def count_time_maxHeapPriorityqueue():
global nums, max_heap_PriorityQueue, record_list1
for i in range(nums):
max_heap_PriorityQueue.enqueue(np.random.randint(1000))
for i in range(nums):
record_list1.append(max_heap_PriorityQueue.dequeue())
@count_time
def count_time_arrPriorityqueue():
global nums, arr_PriorityQueue, record_list2
for i in range(nums):
arr_PriorityQueue.enqueue(np.random.randint(1000))
for i in range(nums):
record_list2.append(arr_PriorityQueue.dequeue())
if __name__ == '__main__':
print('基于最大堆的优先队列用时:')
count_time_maxHeapPriorityqueue()
print('基于数组的优先队列用时:')
count_time_arrPriorityqueue()
print('检测:') # 既然每次都在尾部添加优先队列中最大的元素(权重最高的元素),那么list应该是严格降序排列的
check_list(record_list1, True) # 检测基于最大堆的优先队列
check_list(record_list2, False) # 检测基于数组的优先队列
四、输出
基于最大堆的优先队列用时:
共用时: 0.420652 秒
基于数组的优先队列用时:
共用时: 9.073835 秒
检测:
Priority Queue which is based on the MaxHeap test completed.
Priority Queue which is based on the Array test completed.
五、总结
- 可以看到两种不同的底层实现的性能差距还是非常明显的,随着操作量的进一步增多,性能差距将更加明显。一般的优先队列的底层都是基于最大(小)堆来实现的。
- 本节只用整数作为测试样例,只是为了方便,小伙伴可以向优先队列中加入任何支持比较操作的对象,那样才能真正的看到优先队列的作用。它的用处真的非常广泛。
若有还可以改进、优化的地方,还请小伙伴们批评指正!