用堆实现优先级队列(Priority Queue)

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


猜你喜欢

转载自blog.csdn.net/qq_38612955/article/details/79922161