二叉堆代码实现
♟️

二叉堆代码实现

Date
Property
二叉堆Python代码实现
Created
Jun 13, 2022 11:17 AM
Tags
数据结构
二叉堆

代码实现

# 二叉堆实现 # 采用“完全二叉树”的结构近似实现“平衡”的二叉树 # 对于完全二叉树,使用单个列表就可以实现完全树,如果节点在列表中的下标为p,其左节点下标为2p,右节点下标为2p+1;节点n的父节点为n//2 # 最小堆实现 class BinHeap: def __init__(self): self.heapList = [0] self.currentSize = 0 def percUp(self, i): while i // 2 > 0: # 如果新插入的值小于根节点值,进行替换 if self.heapList[i] < self.heapList[i // 2]: tmp = self.heapList[i] self.heapList[i] = self.heapList[i // 2] self.heapList[i // 2] = 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) # 如果根节点值大于子节点值,进行替换 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.heapList[i * 2 + 1]: return i * 2 else: return i * 2 + 1 def delMin(self): # 删除操作:将最后一个元素和第一个元素进行替换,再删除第一个元素,再跑一遍percDown进行调整 retval = self.heapList[1] self.heapList[1] = self.heapList[self.currentSize] self.currentSize -= 1 self.heapList.pop() self.percDown(1) return retval def bulidHeap(self, alist): # 需要自上而下进行构建,不是一个叠加过程,而是一个递归过程 # 再调整上层元素时,并不需要同下层所有元素作比较,只需要比较其中一个分支。同时这又是一个递归过程; # 生成二叉堆需要O(n),关键在于对于许多buildHeap操作而言,树的高度要比logN要小 i = len(alist) // 2 self.currentSize = len(alist) self.heapList = [0] + alist while i > 0: self.percDown(i) # 自下而上进行对比 i -= 1

简单应用

Python中有函数将数组转化为最小堆,转化时间复杂度为O(n)
heapq.heapify(q) # Python提供了最小堆的实现函数,时间复杂度为O(n) d, x, y = heapq.heappop(q) heapq.heappush(q, (-diff(x + 1, y + 1), x + 1, y + 1)) # 插入新数据

例题

class Solution: def smallestK(self, arr: List[int], k: int) -> List[int]: if k == 0: return list() hp = [-x for x in arr[:k]] heapq.heapify(hp) for i in range(k, len(arr)): if -hp[0] > arr[i]: heapq.heappop(hp) heapq.heappush(hp, -arr[i]) ans = [-x for x in hp] return ans

参考

为什么建堆的时间复杂度是O(n)?_LeoSha的博客-CSDN博客
如果仅从代码上直观观察,会得出构造二叉堆的时间复杂度为O(n㏒n)的结果,这个结果是错的,虽然该算法外层套一个n次循环,而内层套一个分治策略下的㏒n复杂度的循环,该思考方法犯了一个原则性错误,那就是构建二叉堆是自下而上的构建,每一层的最大纵深总是小于等于树的深度的,因此,该问题是叠加问题,而非递归问题。那么换个方式,假如我们自上而下建立二叉堆,那么插入每个节点都和树的深度有关,并且都是不断的把树折半来实现插入,因此是典型的递归,而非叠加。 在做证明之前,我们的前提是,建立堆的顺序是bottom-top的。 正确的证明方法应当如下: 具有n个元素的平衡二叉树,树高为㏒n,我们设这个变量为h。 最下层非叶节点的元素,只需做一次线性运算便可以确定大根,而这一层具有2^(h-1)个元素,我们假定O(1)=1,那么这一层元素所需时间为2^(h-1) × 1。 由于是bottom-top建立堆,因此在调整上层元素的时候,并不需要同下层所有元素做比较,只需要同其中之一分支作比较,而作比较次数则是树的高度减去当前节点的高度。因此,第x层元素的计算量为2^(x) × (h-x)。 又以上通项公式可得知,构造树高为h的二叉堆的精确时间复杂度为: S = 2^(h-1) × 1 + 2^(h-2) × 2 + ...... +1 × (h-1) ① 通过观察第四步得出的公式可知,该求和公式为等差数列和等比数列的乘积,因此用错位想减发求解,给公式左右两侧同时乘以2,可知: 2S = 2^h × 1 + 2^(h-1) × 2+ ......
为什么建堆的时间复杂度是O(n)?_LeoSha的博客-CSDN博客