python实现堆插入、删除、创建

什么是堆

 堆是一种数据结构,它可以看做一棵完全二叉树。但是它又是被存储成数组形式的。堆又分为最大堆和最小堆。
最大堆:一棵完全二叉树的任意一个节点都大于等于它的左右子节点(如果有的话)
最小堆:一棵完全二叉树的任意一个节点都小于等于它的左右子节点(如果有的话)
我们以最小堆为例。
如图:
这里写图片描述
 上图是一棵完全二叉树,同时也是最小堆。它的任意节点都小于等于它的子节点。如果用数组表示的话,就用层次遍历的方式,表示为[5, 9, 11, 14, 18, 19, 21, 33, 17, 27]。

因为是一棵完全二叉树,所以堆有一些特性。
1.最小(大)堆的根节点(第一个元素)是整棵数最小(大)的。
2.除根节点外,节点k的父节点索引是k/2,左右子节点(如果有的话)为2k和2k+1。注:索引从1开始,而非0

所以堆也是一种优先队列,我理解的优先队列,就是它不是一个完全有序的队列,但是又有一定的顺序,例如最大(小)能一下找到。所以是一种有着优先级别的队列。

堆的实现(以最小堆为例,最大堆同理):

1.插入(insert)

插入的过程是,先将新的节点放在最末尾,然后依次与它的父节点比较,如果比父节点小,那么就交换位置。一直到比它的父节点大为止。这个过程称为上浮

这里写图片描述
上图的过程:
1.原数组为[5, 9, 11, 14, 18, 19, 21, 33, 17, 27],插入新的元素[5, 9, 11, 14, 18, 19, 21, 33, 17, 27,7]
2.比较7和它的父节点18, 7<18,交换它们的位置。[5, 9, 11, 14, 7, 19, 21, 33, 17, 27,18]
3.再比较7和它的父节点9. 7<9,交换。[5, 7, 11, 14, 9, 19, 21, 33, 17, 27,18]
4.再比较7和它的父节点5. 7>5, 停止交换,7已经到了正确的位置。所以最终的树为[5, 7, 11, 14, 9, 19, 21, 33, 17, 27,18]

用代码实现:

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

    def percUp(self, index):      #实现上浮
        while index // 2 > 0 and self.heapList[index//2] > self.heapList[index]:
            self.heapList[index//2], self.heapList[index] = self.heapList[index], self.heapList[index//2]
            index = index // 2

    def insert(self, k):
        self.heapList.append(k)
        self.currentSize += 1
        self.percUp(self.currentSize)   

2.删除(delMin)

 堆的删除一般都是删除根节点。具体实现是,先把根节点(最小值)删除,然后把列表的最后一项移到根节点的位置。这样还是一棵完整的二叉树。接下来调节这个新的根节点的位置。与它的左右子节点比大小。这个过程叫做下沉
这里写图片描述
如上图过程:
1. 删除根节点5,然后把末尾的节点27移到根节点。由[5, 9, 11, 14, 18, 19, 21, 33, 17, 27]变成[27, 9, 11, 14, 18, 19, 21, 33, 17]
2. 根节点27,和左右节点9,11相比,最小的是9,所以27与9交换位置。变为[9, 27, 11, 14, 18, 19, 21, 33, 17]
3. 27和它的左右节点14,18相比,最小的是14,所以27和14交换。变为[9, 14, 11, 27, 18, 19, 21, 33, 17]
4. 27和它的左右节点17,33相比,最小的是17,所以27和17交换。变为[9, 14,11, 17, 18, 19, 21, 33, 27]
5. 27没有子节点了。到达了最终位置。

实现代码:

    def minChild(self, i):
        if i * 2 + 1 > self.currentSize:
            return 2 * i
        return 2 * i if (self.heapList[2*i] < self.heapList[2*i + 1]) else 2*i+1

    def percDown(self, index):
        while index * 2 <= self.currentSize:
            mc = self.minChild(index)
            if self.heapList[index] > self.heapList[mc]:
                self.heapList[index], self.heapList[mc] = self.heapList[mc], self.heapList[index]
            index = mc

    def delMin(self):
        self.heapList[1] = self.heapList[-1]
        self.heapList.pop()
        self.currentSize -= 1
        self.percDown(1)

3.构建堆(buildHeap)

 这个函数的含义是,传入一个列表,将它变成一个最小堆。我们可以一个一个地插入(insert),但是那样太低效了。还有一种比较高效的方式如下:
 先将整个列表传入self.heapList。变成一棵树,然后开始调节树中节点位置。假设根节点的位置是k,也就是说k的父节点是k//2。同理,最后一个有子节点的节点就是k//2。这个过程有点绕。下面用一个例子来解释,如图:
这里写图片描述
我用红色标注的是它们的编号,最末的节点编号是5,它的父节点是2。也就是说2以后的节点都是叶节点。我们调整节点位置的时候,以父节点+两个子节点为整体。因为在堆中,节点的关系都是根据父节点来定义的。
1.找到最末节点位置k,它的父节点是i = k//2。这个时候我们开始以下沉的方式调节它的父节点。
2.然后依次调节i-1、i-2….1。这样就能保证所有的父节点都在正确的位置上了。

实现代码:

    def buildHeap(self, alist):
        self.currentSize = len(alist)
        self.heapList = [0] + alist
        i = self.currentSize // 2
        while i > 0:
            self.percDown(i)
            i -= 1

总代码:

class BinHeap:       #定义堆
    def __init__(self):
        self.heapList = [0]     #堆有两个属性,堆的元素和长度,其中先把位置0用0填充。这样就能使堆从1开始索引
        self.currentSize = 0

    def percUp(self, index):       #实现堆的上浮,目的是一直跟自身的父节点比较。
        while index // 2 > 0 and self.heapList[index//2] > self.heapList[index]:
            self.heapList[index//2], self.heapList[index] = self.heapList[index], self.heapList[index//2]
            index = index // 2

    def insert(self, k):           #借助上浮实现插入
        self.heapList.append(k)
        self.currentSize += 1
        self.percUp(self.currentSize)

    def minChild(self, i):         #查找子节点中最小的那个
        if i * 2 + 1 > self.currentSize:
            return 2 * i
        return 2*i if (self.heapList[2*i] < self.heapList[2*i + 1]) else 2*i+1

    def percDown(self, index):     #实现下沉
        while index * 2 <= self.currentSize:
            mc = self.minChild(index)
            if self.heapList[index] > self.heapList[mc]:
                self.heapList[index], self.heapList[mc] = self.heapList[mc], self.heapList[index]
            index = mc

    def delMin(self):            #删除最小的元素
        res = self.heapList[1]
        self.heapList[1] = self.heapList[-1]
        self.heapList.pop()
        self.currentSize -= 1
        self.percDown(1)
        return res

    def buildHeap(self, alist):   #由列表创建二叉堆
        self.currentSize = len(alist)
        self.heapList = [0] + alist
        i = self.currentSize // 2
        while i > 0:
            self.percDown(i)
            i -= 1

猜你喜欢

转载自blog.csdn.net/lisa_ren_123/article/details/81161869