1.优先级队列定义:
优先级队列(priority queue) 是0个或多个元素的集合,每个元素都有一个优先权,对优先级队列执行的操作有
(1)查找
(2)插入一个新元素
(3)删除 一般情况下,查找操作用来搜索优先权最大的元素,删除操作用来删除该元素 。
对于优先权相同的元素,可按先进先出次序处理或按任意优先权进行。
2.优先级队列的实现方案
优先级队列的实现既要兼顾成本,也要兼顾效率
(1).第一种使用向量实现:
由图可知,在使用插入操作时,时间复杂度为o(1),但是在getMax,需要遍历整个向量,找出最大的元素,
而delMax操作时却不仅要遍历向量找到最大元素,而且在摘抄它之后将所有元素顺序后移,这种时间复杂度是无法接受的。
(2).使用有序向量
在使用有序向量时,getMax操作和delMax操作都只有常数时间,但是在插入操作时,先要对其进行二分查找,找到插入的位置,然后在提前将它的后继依次后移,维护有序向量的有序性.这种方式时间复杂度太高,无法接受。
(3).使用列表
在使用列表构建优先级队列时,insert操作只需在队尾将指针指向待插入元素。但是在getMax操作中,需要遍历整个列表。而在delMax操作时,先要找到最大值元素,然后在将前驱的指针指向后继,完成删除操作。所以这种方式时间复杂度较高。
(4)使用有序列表
在使用有序向量时,也面临着时间复杂度较高的情况。在进行insert操作时,需要找到待插入元素的前驱,然后将前驱的指针指向自己,自己的指针指向后继。
(5)使用BBST
使用BBST构建优先级队列时,无论是插入,查找还是删除,都有非常优秀,但是优先级队列却不需要使用到全部的BBST。
(6)使用堆来实现优先级队列
堆是一种物理结构上用数组实现,逻辑结构为一颗完全二叉树的数据结构。堆的定义是父节点元素必定大于(小于)它的左孩子节点或者右孩子节点,(大于为大根堆,小于为小根堆)这样根节点元素则必然是整个堆的最大(最小)值。
以下是一个值为,1.2.3.4.5的小根堆的构成(因为我找到不到如何设置成大根堆.不过原理是一样的)
由图可知堆的逻辑结构和物理结构,下面给出具体的代码实现
3.具体实现
首先先给出它的insert操作
def insert(self,value):
self._arr.append(value) #将新值插入到堆尾
self._index += 1 #index++ index代表了value的下标
index=self._index
if index % 2 == 0:
parent = index // 2 - 1
else:
parent = index // 2 #获取index的父节点
temp=self._arr[index] #获取value的值
#上滤
while parent>=0: #如果没超过根节点 也就是index的父节点最大只能为0号下标
if temp<self._arr[parent]: #如果下标index的值也就是value小于它的父节点,没破坏堆序性
break
#print(index,parent)
self._arr[index] = self._arr[parent]#不然的话将它的父节点的值赋给index
index=parent #index的父节点成为新的待调整结点
if index%2==0:
parent = index // 2-1
else:
parent=index//2#取得新index的父节点
self._arr[index]=temp#循环结束后将temp也就是value的值给最后停下来的index
return
getMax操作
def getMax(self):
if self._index == -1: #如果_index为-1 则堆为空
return
self.__swap(0,self._index,self._arr)#不然交换堆顶元素和最后的元素
maxValue=self._arr[self._index] #交换后取得最后一个元素
self.__remove() #删除最后一个元素
return maxValue #返回最大值
remove操作
def __remove(self):
if self._index==-1: #如果_index为-1 则堆为空
return
self._arr.pop() #删除最后一个元素
self._index-=1
self.buildHeap(0,self._arr)#因为目前堆顶元素是未知的,所以要进行一次调整
return
调整操作
def buildHeap(self,index,_arr):
if self._index==-1: #如果堆里没有原始 则返回
return
temp=_arr[index]#temp代表index下标的值
k=(index*2)+1 #k代表index的左子树
lenth=len(_arr)-1
while k<=lenth:
if k<lenth and _arr[k]<_arr[k+1] :#如果k的左子树小于length(也就是k还有右子树)并且它的右子树大于它
k+=1 #k++ k指向了右子树
if _arr[k]<temp: #如果temp大于当前子树的最大值(无论左子树还是右子树都大于,因为上个判断已经判断了左右子树大小)
break #直接返回
_arr[index]=_arr[k] #如果temp不大于左子树或者右子树的值 将K结点(index子树)的值赋给inedx结点值
index=k #要调整的结点变为K 因为index结点已经判断完了
k=(k*2)+1 #K变成index的左子树
_arr[index]=temp #如果循环结束,说明调整完毕,最后index的值是将是它的子树的值,需要修改为value也就是temp的值
测试结果
输入:
l=[11,2,3,4,5,6]
pq=PriorityQueue()
print('当前堆元素为',pq.get_arr())
print('获取最大值',pq.getMax())
print('当前堆元素为',pq.get_arr())
pq.insert(14)
print('当前堆元素为',pq.get_arr())
print('获取的最大值',pq.getMax())
print('获取的最大值',pq.getMax())
print('当前堆元素为',pq.get_arr())
输出:
当前堆元素为 [11, 5, 6, 4, 2, 3]获取最大值 11
当前堆元素为 [6, 5, 3, 4, 2]
当前堆元素为 [14, 5, 6, 4, 2, 3]
获取的最大值 14
获取的最大值 6
当前堆元素为 [5, 4, 3, 2]
进程已结束,退出代码0
完整版代码
#优先级队列 2018-01-06
class PriorityQueue:
_arr=[]
_index=-1
def __init__(self,arr=[]):
self._arr=arr
self._index=len(self._arr)-1#指向最后一个元素
self.firstBuildHeap()
#index代表要进行向下调整的结点
def buildHeap(self,index,_arr):
if self._index==-1: #如果堆里没有原始 则返回
return
temp=_arr[index]#temp代表index下标的值
k=(index*2)+1 #k代表index的左子树
lenth=len(_arr)-1
while k<=lenth:
if k<lenth and _arr[k]<_arr[k+1] :#如果k的左子树小于length(也就是k还有右子树)并且它的右子树大于它
k+=1 #k++ k指向了右子树
if _arr[k]<temp: #如果temp大于当前子树的最大值(无论左子树还是右子树都大于,因为上个判断已经判断了左右子树大小)
break #直接返回
_arr[index]=_arr[k] #如果temp不大于左子树或者右子树的值 将K结点(index子树)的值赋给inedx结点值
index=k #要调整的结点变为K 因为index结点已经判断完了
k=(k*2)+1 #K变成index的左子树
_arr[index]=temp #如果循环结束,说明调整完毕,最后index的值是将是它的子树的值,需要修改为value也就是temp的值
def firstBuildHeap(self):
index=(len(self._arr)//2)-1
while index>=0:
self.buildHeap(index,self._arr)
index-=1
def get_arr(self):
return self._arr
def getMax(self):
if self._index == -1: #如果_index为-1 则堆为空
return
self.__swap(0,self._index,self._arr)#不然交换堆顶元素和最后的元素
maxValue=self._arr[self._index] #交换后取得最后一个元素
self.__remove() #删除最后一个元素
return maxValue #返回最大值
def __remove(self):
if self._index==-1: #如果_index为-1 则堆为空
return
self._arr.pop() #删除最后一个元素
self._index-=1
self.buildHeap(0,self._arr)#因为目前堆顶元素是未知的,所以要进行一次调整
return
def insert(self,value):
self._arr.append(value) #将新值插入到堆尾
self._index += 1 #index++ index代表了value的下标
index=self._index
if index % 2 == 0:
parent = index // 2 - 1
else:
parent = index // 2 #获取index的父节点
temp=self._arr[index] #获取value的值
#上滤
while parent>=0: #如果没超过根节点 也就是index的父节点最大只能为0号下标
if temp<self._arr[parent]: #如果下标index的值也就是value小于它的父节点,没破坏堆序性
break
#print(index,parent)
self._arr[index] = self._arr[parent]#不然的话将它的父节点的值赋给index
index=parent #index的父节点成为新的待调整结点
if index%2==0:
parent = index // 2-1
else:
parent=index//2#取得新index的父节点
self._arr[index]=temp#循环结束后将temp也就是value的值给最后停下来的index
return
def __swap(self,i,j,arr):
k=arr[i]
arr[i]=arr[j]
arr[j]=k