数据结构与算法(python):优先队列和二叉堆

本文介绍了如何使用Python实现优先队列,重点讲解了二叉堆及其作为优先队列的数据结构优势。通过二叉堆(最小堆)的`insert`、`delMin`等方法确保操作复杂度为O(logn)。同时,详细阐述了完全二叉树的概念,并展示了如何构建和维护堆的性质。代码示例展示了二叉堆的初始化、插入、删除最小元素以及从列表构建堆的过程。
摘要由CSDN通过智能技术生成

参考自 MOOC数据结构与算法Python版

一、优先队列

队列有一种变体称为“优先队列”,优先队列的出队跟队列一样从队首出队,但在优先队列内部, 数据项的次序却是由“优先级”来确定:高优先级的数据项排在队首,而低优先级的数据项则排在后面。这样,优先队列的入队操作就比较复杂,需要将数据项根据其优先级尽量挤到队列前方。
有什么方案可以用来实现优先队列?

1.1 二叉堆Binary Heap实现优先队列

二叉堆能够将优先队列的入队出队复杂度都保持在 O ( l o g n ) O(log n) O(logn)。二叉堆的有趣之处在于, 其逻辑结构上像二叉树, 却是用非嵌套的列表来实现的

  • 最小key排在队首的称为“最小堆min heap”
  • 最大key排在队首的是“最大堆max heap”。

ADT BinaryHeap的操作定义如下:

函数含义
BinaryHeap()创建一个空二叉堆对象
insert(k)将新key加入到堆中
findMin()返回堆中的最小项,最小项仍保留在堆中
delMin()返回堆中的最小项,同时从堆中删除
isEmpty()返回堆是否为空
size()返回堆中key的个数
buildHeap(list)从一个key列表创建新堆

【代码】:

from pythonds.trees.binheap import BinHeap
bh = BinHeap()
bh.insert(5)
bh.insert(7)
bh.insert(3)
bh.insert(11)
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())
print(bh.delMin())

为了使堆操作能保持在对数水平上, 就必须采用二叉树结构;
同样, 如果要使操作始终保持在对数数量级上, 就必须始终保持二叉树的“平衡”
– 树根左右子树拥有相同数量的节点
我们采用“完全二叉树”的结构来近似实现“平衡”

1.2 完全二叉树

叶节点最多只出现在最底层和次底层,而且最底层的叶节点都连续集中在最左边,每个内部节点都有两个子节点, 最多只有1个节点例外,比如下图中的18号节点。
如果节点的下标为p,那么其左子节点下标为2p,右子节点为2p+1,其父节点下标为p//2在这里插入图片描述任何一个节点x, 其父节点p中的key均小于x中的key,这样,符合“堆”性质的二叉树,其中任何一条路径,均是一个已排序数列, 根节点的key最小。

二、二叉堆的实现

堆的性质:在任何一条路径上,它都是一个有序的队列。

2.1 初始化

采用一个列表来保存堆数据,其中表首下标为0的项无用,但为了后面代码可以用到简单的整数乘除法,仍保留它。

class Binheap:
    def __init__(self):
        self.heapList = [0]
        self.currentSize = 0

insert(key)方法

  • 首先,为了保持“完全二叉树”的性质,新key应该添加到列表末尾。会有问题吗?
    新key加在列表末尾,显然无法保持“堆”次序,即路径上的顺序被破坏。虽然对其它路径的次序没有影响,但对于其到根的路径可能破坏次序。
    在这里插入图片描述需要将新key沿着路径来上浮”到其正确位置。
    注意:新key的“上浮”不会影响其它路径节点的“堆”次序
    在这里插入图片描述
def percUp(self,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 = self.currentSize + 1  #size+1
    self.percUp(self.currentSize)  # 上浮,比较大小

delMin()方法:移走整个堆中最小的key

移走根节点heapList[1],并保证堆次序不被破坏。
先用最后一个节点来代替根节点,再将新的根节点沿着一条路径“下沉”,直到比两个子节点都小
“下沉”路径的选择:如果比子节点大,那么选择两个子节点中较小的子节点交换下沉。示 意图如下:

  1. 左子节点9,右子节点 11,27和9交换。
  2. 左子节点14,右子节点 18,27和14交换。
  3. 左子节点33,右子节点 17,27和17交换。
    在这里插入图片描述【代码】:
def percDown(self, i):
    while (i*2) <= self.currentSize:
        mc = self.minChild(i) #找出较小的子节点
        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
    else:
        if self.heapList[i*2] < self.hepList[i*2+1]:
            return i*2
        else:
            return i*2+1

def delMin(self):
    retval = self.heapList[1]#移走堆顶
    self.heapList[1] = self.heapList[self.currenSize]
    self.currentSize -= 1
    self.heapList.pop()
    self.percDown(1) #新顶下沉
    return retval

buildHeap(lst)方法:从无序表生成“堆”

下沉法,能够将总代价控制在 O ( n ) O(n) O(n)
【代码】:

def buildHeap(self, alist):
    i = len(alist)//2 #从最后节点的父节点开始
    self.currentSize = len(alist)
    self.heapList = [0] + alist[:]
    print(len(self.heapList),i)
    while (i>0):
        print(self.heapList, i)
        self.percDown(i)
        i -= 1
    print(self.heapList, i)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值