python--二叉堆实现优先队列

优先队列

  • 队列:FIFO,先进先出

  • 优先队列:队列中每个元素都分配一个优先权值,队列的出队顺序按照优先权值来划分,高者优先或者低者优先。这样优先队列必须有两个基本操作:入队和(按照优先权值)出队(以优先级高的先出来),简单点说就是插入或者删除元素的时候,元素自动排序,实现原理二叉堆的操作

  • 应用:打印机的任务调度和操作系统的进程调度。

  • 优先想到的就是用排序函数和队列的一些简单方法来实现优先队列。但是在列表中插入一个数据的复杂度是O(n),因为你插入的时候要移动数据,列表的排序复杂度是O(nlogn),我们可以用别的方法来降低复杂度。比如:二叉堆实现优先队列,二叉堆可以将复杂度保持在O(logn)。置于为什么请看二叉堆的结构。

二叉堆

  • 特殊的二叉树,对一般的二叉树提出了结构性和堆序性的要求

    1. 结构性:二叉堆是一个完全二叉树(除最后一层每层结点达到最大值,最后一层叶子节点连续集中在左侧)

    2. 堆序性:大根堆:所有结点值大于其后裔结点值 。 (父大于子)
      小根堆:所有节点值均小于其后裔结点值。(父小于子)

    3. 使用单列表实现二叉树,对于结点列表中下标为p,那么左子节点为2p,右节点2p+1

      举个例子(如图):

【注意】数组的第一个索引0空着不用,通常我们把第一个元素设置为0,结点i的儿子是2i和2i+1

class BinHeap:
    def __init__(self):
        self.heapList=[0]
        self.currentSize=0#完全树的单列表表示,以及下标,记录堆的大小
	def buildHeap(self,alist):#从列表生成二叉堆
	        i = len(alist)//2 # 优化(至于为什么暂时没搞懂)
	        self.currentSize=len(alist)
	        self.heapList = [0]+alist[:]#i永远不会得0,不会访问到这个元素列表从0访问,树从1,添加0后保证了树的性质
	        while(i>0):
	            self.percDown(i)
	            i-=1

堆的删除操作==优先队列的出队

  • 每次出队操作==返回堆顶元素+删除堆顶元素,删除堆顶元素后需要保证二叉堆的结构性和堆序性,这个操作过程叫做堆调整
  • 出队删除堆顶元素然后让最后一个元素补充,此时需要调整(下沉)
    如下方所示:
    python--二叉堆实现优先队列_第1张图片

python--二叉堆实现优先队列_第2张图片

python--二叉堆实现优先队列_第3张图片

def percDown(self,i):#下沉
        while (i*2)<=self.currentSize:#循环下沉直到结束
            mc = self.minChild(i)#记录两个孩子的最小值的位置mc
            if self.heapList[i]>self.heapList[mc]:#比较孩子和父亲的大小
                tmp = self.heapList[i]#如果父亲大,交换使得大数下沉
                self.heapList[i]=self.heapList[mc]
                self.heapList[mc] = tmp
            i=mc
    def minChild(self,i):#返回两个子节点的最小值的位置
        if i*2+1>self.currentSize:
            return i*2 #和percdown中的while条件相呼应,说明结束
        else:
            if self.heapList[i*2]<self.heapList[i*2+1]:
                return i*2
            else:
                return i*2+1
    def delMin(self):#返回堆中的最小项同时删除
        retval = self.heapList[1]#记录最小值
        self.heapList[1] = self.heapList[self.currentSize]#将最后一个值放在根部
        self.currentSize-=1
        self.heapList.pop()#结点释放掉
        self.percDown(1)#根部元素需要调整为满足堆得性质
        return retval

堆的插入操作==优先队列的入队

  • 入队——插入到末尾进行调整简称(上浮)
    如图感受一波:
    python--二叉堆实现优先队列_第4张图片
    python--二叉堆实现优先队列_第5张图片
 def percUP(self,i):#小数上浮,i为下标
        while i//2>0:#不断上浮
            if self.heapList[i]<self.heapList[i//2]:#新节点小于父子节点
                tmp = self.heapList[i//2]
                self.heapList[i//2] = self.heapList[i]
                self.heapList[i] = tmp     #交换
        i=i//2#继续和上面的比较
    def insert(self,k):#插入结点的值
        self.heapList.append(k)
        self.currentSize+=1
        self.percUP(self.currentSize)#调整堆得次序小数上浮

整体代码

'''
coding=utf-8
'''
class BinHeap:
    def __init__(self):
        self.heapList=[0]
        self.currentSize=0#完全树的单列表表示,以及下标,记录堆的大小
    def percUP(self,i):#小数上浮,i为下标
        while i//2>0:#不断上浮
            if self.heapList[i]<self.heapList[i//2]:#新节点小于父子节点
                tmp = self.heapList[i//2]
                self.heapList[i//2] = self.heapList[i]
                self.heapList[i] = tmp     #交换
        i=i//2#继续和上面的比较
    def insert(self,k):#插入结点的值
        self.heapList.append(k)
        self.currentSize+=1
        self.percUP(self.currentSize)#调整堆得次序小数上浮
    
    def percDown(self,i):#下沉
        while (i*2)<=self.currentSize:#循环下沉直到结束
            mc = self.minChild(i)#记录两个孩子的最小值的位置mc
            if self.heapList[i]>self.heapList[mc]:#比较孩子和父亲的大小
                tmp = self.heapList[i]#如果父亲大,交换使得大数下沉
                self.heapList[i]=self.heapList[mc]
                self.heapList[mc] = tmp
            i=mc
    def minChild(self,i):#返回两个子节点的最小值的位置
        if i*2+1>self.currentSize:
            return i*2 #和percdown中的while条件相呼应,说明结束
        else:
            if self.heapList[i*2]<self.heapList[i*2+1]:
                return i*2
            else:
                return i*2+1
    def delMin(self):#返回堆中的最小项同时删除
        retval = self.heapList[1]#记录最小值
        self.heapList[1] = self.heapList[self.currentSize]#将最后一个值放在根部
        self.currentSize-=1
        self.heapList.pop()#结点释放掉
        self.percDown(1)#根部元素需要调整为满足堆得性质
        return retval
    def buildHeap(self,alist):#从列表生成二叉堆
        i = len(alist)//2 # 优化(至于为什么暂时没搞懂)
        self.currentSize=len(alist)
        self.heapList = [0]+alist[:]#i永远不会得0,不会访问到这个元素列表从0访问,树从1,添加0后保证了树的性质
        while(i>0):
            self.percDown(i)
            i-=1

#举个例子感受一下吧
bh = BinHeap()
bh.buildHeap([9,5,6,2,3])
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())

【注】所有的问题都出在写代码上,不写代码逼事没有(所以有问题看注释,哈哈)

你可能感兴趣的:(python--二叉堆实现优先队列)