python二叉堆

目录

定义

堆操作

插入节点

删除根节点

构造二叉堆

合并两个二叉堆

堆排序


定义

二叉堆是一种特殊的堆,二叉堆是完全二元树二叉树)或者是近似完全二元树(二叉树)。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;

最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

二叉堆一般用数组来表示。如果根节点在数组中的位置是1,第n个位置的子节点分别在2n和 2n+1。因此,第1个位置的子节点在2和3,第2个位置的子节点在4和5。以此类推。这种基于1的数组存储方式便于寻找父节点和子节点。

二叉堆的根节点叫做堆顶

最大堆和最小堆的特点,决定了在最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素

二叉堆虽然是一颗完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组当中

堆操作

插入节点

在数组的最末尾插入新节点。然后自下而上调整子节点与父节点,比较当前节点与父节点,不满足堆性质则交换。从而使得当前子树满足二叉堆的性质。时间复杂度

 

 

删除根节点

删除根节点用于堆排序

对于最大堆,删除根节点就是删除最大值;对于最小堆,是删除最小值。然后,把堆存储的最后那个节点移到填在根节点处。再从上而下调整父节点与它的子节点:对于最大堆,父节点如果小于具有最大值的子节点,则交换二者。这一操作称作down-heap或bubble-down, percolate-down, sift-down, trickle down, heapify-down, cascade-down,extract-min/max等。直至当前节点与它的子节点满足堆性质为止。

构造二叉堆

构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质上就是让所有非叶子节点依次下沉

一个直观办法是从单节点的二叉堆开始,每次插入一个节点。其时间复杂度为

最优算法是从一个节点元素任意放置的二叉树开始,自底向上对每一个子树执行删除根节点时的Max-Heapify算法(这是对最大堆而言)使得当前子树成为一个二叉堆。具体而言,假设高度为h的子树均已完成二叉堆化,那么对于高度为h+1的子树,把其根节点沿着最大子节点的分枝做调整,最多需要h步完成二叉堆化。可以证明,这个算法的时间复杂度为O(n)。

合并两个二叉堆

最优方法是把两个二叉堆首尾相连放在一个数组中,然后构造新的二叉堆。时间复杂度,其中n、k为两个堆的元素数目。

如果经常需要合并两个堆的操作,那么使用二项式堆更好,其时间复杂度为

堆排序

算法步骤

  • 将无序数组构造成二叉堆。如果需要从小到大排序,则构造最大堆,如果需要从大到小排序,则构造最小堆
  • 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。
    代码实现
    可以删除insert和delMin函数,增加Sort函数
class MinHeap:

    def __init__(self):
        self.heap = []

    #上浮函数,通过对末尾元素进行上浮,调整堆状态,最终为最小堆
    def shift_up(self):
        child_index = len(self.heap)-1
        father_index = (child_index-1)//2  #整除可以保证无论是左子叶还是右子叶都能定位到父节点
        temp = self.heap[child_index]
        while child_index > 0 and temp < self.heap[father_index]:  #子节点大于父节点,需要上浮
            self.heap[child_index] = self.heap[father_index]
            child_index = father_index     #这是单个节点的上浮操作,所以直接定位到新位置,不考虑其他
            father_index = (child_index-1)//2
        self.heap[child_index] = temp    #循环后赋值,可减少操作,child_index是原fatherindex

    #下沉函数,删除根节点后,末尾节点成为根节点,然后执行下沉操作
    def shift_down(self,father_index):
        child_index = 2*father_index +1 #左孩子
        temp = self.heap[father_index]  #用于最后赋值,减少操作
        while child_index < len(self.heap):
            #右子叶最小,根节点右下沉
            if self.heap[child_index] > self.heap[child_index+1]:
                child_index = child_index +1
            #根节点已经最小,不用下沉
            if temp < self.heap[child_index]:
                break
            self.heap[father_index] = self.heap[child_index]
            father_index = child_index
            child_index = 2*father_index +1 #左孩子
        self.heap[father_index] = temp  #最后赋值 father_index是原child_index

    #下沉函数,删除根节点后,末尾节点成为根节点,然后执行下沉操作
    def shift_down_len(self,father_index,length):
        child_index = 2*father_index +1 #左孩子
        temp = self.heap[father_index]  #用于最后赋值,减少操作
        while child_index < length:
            #右子叶最小,根节点右下沉
            if self.heap[child_index] > self.heap[child_index+1]:
                child_index = child_index +1
            #根节点已经最小,不用下沉
            if temp < self.heap[child_index]:
                break
            self.heap[father_index] = self.heap[child_index]
            father_index = child_index
            child_index = 2*father_index +1 #左孩子
        self.heap[father_index] = temp  #最后赋值


    #构造堆,从底层开始,对每一个非叶子节点进行下沉
    def buildheap(self,alist):
        self.heap = alist
        for i in range((len(alist)-2)//2, -1,-1):   #1,2,3,4,5  (4-1)//2 = 1  (5-2)//2 = 1  4,5的根节点是索引为1 的2
            self.shift_down(i)

    def insertheap(self,node):
        self.heap.append(node)
        self.shift_up()

    def popheap(self):
        temp = self.heap[0]
        self.heap[0] = self.heap.pop()
        self.shift_down(0)
        return temp

    def sortheap(self,alist):
        self.buildheap(alist)
        for i in range(len(alist)-1,-1,-1):
            temp = self.heap[0]
            self.heap[0] = self.heap[i]
            self.heap[i] = temp
        self.shift_down_len(0,i)
        return self.heap

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

海人001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值