python数据结构——利用堆实现优先级队列

之前写过一个有序/无序列表来实现优先级队列的,在这里
这次,我们尝试一下用堆来实现。

首先的话,这里默认的堆是小顶堆。
堆,是二叉树的一种,更准确来说是完全二叉树,所以经常使用数组(python中用列表)来存储。如果对这一部分有问题,建议看这里

堆的性质就是根部的结点最小(这里默认小顶堆),因为树的递归性质,对于每一个子树都成立。

堆支持插入和删除,一般删除都是指删除根节点,并且认为在操作之前堆是满足性质的。

插入

我们习惯的是先在列表的最后进行插入,然后此时一般会破坏堆的性质,我们就需要进行整理。整理的方法也很容易想到,破坏了也就是插入的叶子节点小于其根节点

插入的一定是叶子,这个不用说了。
不过看一下下面这种情况:
在这里插入图片描述
在进行了一次的交换后,1成了4和5的根节点,但此时仍然不是一个堆,因为3>1,所以我们还需要沿着向根节点的路径一直走下去。

删除

因为删除是默认删除根节点,也就是pop出最小的元素,所以不能直接删除,我们将列表最后一个元素提到根节点,很明显这也是一个破坏了堆结构的行为,所以我们还需要整理。
但这次,是顶上出了问题,所以我们要自顶向下

从上往下选就有问题了,一般都是有两个孩子准备"上位"的,我们就需要选一个小一点的孩子
同样,只处理一处是不合适的,所以我们还是需要沿着较小孩子的路径向下走

初始化

如果是一个从最开始就这样插入删除的,那一定是堆。但就怕半路上要整一个堆,就比较烦。

首先,我们来达成几个共识:

  • 对于一个无序序列要整理成堆,我们应当采用的是自顶向下的整理方式
  • 如果是使用自顶向下的排序方式,我们只需要处理非叶子结点
  • 如果是自顶向下,我们需要从最后一个非叶子结点开始

首先,自顶向下的方式,本身就是通过给定一个子树的根节点,一直往下直到叶子结点,所以是不要处理叶子结点的;
另外,自顶向下本身也就是一条路径,如果是破坏能实现构成堆,但如果是无序的,如果从根节点开始,我们可以看一下这个:
在这里插入图片描述
第一次,将7-3对换,然后走到3位置上(现在为7),然后2-7对换,此时根节点已经结束了,2就没有机会到根节点了。

但是,如果我们采用的是从最后一个非叶子结点,首先是一定能将这个子树整理成堆的(撑死就两个孩子,一定可以的),然后上面一层都的左右孩子都是堆,调用自顶向下就能实现整理了。
细心的可以发现,其实我们在删除的时候,左右子树就都是堆,所以成立。

然后,我们讨论一下自底向上的方式。
我们当时是在最后插入一个结点的情况下,使用了该方法,所以如果处理需要从第一个结点开始,并且因为从底下开始的性质,是一定要到最后一个结点

那么也就相当于是从空堆开始,每次插入一个……,那tm当然能成堆了。
不过,要知道叶子节点数=非叶子结点数(+1),所以这个方法直接把难度拉大了一倍。

实现

有了上面的描述,这部分应该是不难的。

用到了之前的一个基类,也贴出来了。

class PriorityQueueBase():
    """Abstract base class for a priority queue"""
    class _Item():
        """lightweight composite to store priority queue items"""
        __slots__ = '_key', '_value'
        def __init__(self,k,v):
            self._key = k
            self._value = v
        def 
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值