【数据结构】手写堆 HEAP
创始人
2025-01-10 00:38:14
0

heap【堆】掌握

  • 手写上浮、下沉、建堆函数

  • 对一组数进行堆排序

  • 直接使用接口函数heapq

什么是堆???堆是一个二叉树。也就是有两个叉。下面是一个大根堆:

大根堆的每一个根节点比他的子节点都大

有大根堆就有小根堆:

我们可以看到红9在绿9的下一层,大小堆中我们需要注意,【只有根节点对子节点的大小比较】,没有子节点之间的比较。

一、手写函数

def siftup(heap, pos):     endpos = len(heap)     startpos = pos     newitem = heap[pos]     # Bubble up the smaller child until hitting a leaf.     childpos = 2*pos + 1    # leftmost child position     while childpos < endpos:         # Set childpos to index of smaller child.         rightpos = childpos + 1         if rightpos < endpos and not heap[childpos] < heap[rightpos]:             childpos = rightpos         # Move the smaller child up.         heap[pos] = heap[childpos]         pos = childpos         childpos = 2*pos + 1     # The leaf at pos is empty now.  Put newitem there, and bubble it up     # to its final resting place (by sifting its parents down).     heap[pos] = newitem     '''up操作只是将孩子提上去,但是没有保证根节点比孩子小,现在比较孩子和父节点,     如果父节点更大,往下覆盖孩子节点,并且往上继续,比较上面的父节点,直至到头start,     将原来的子节点的值覆盖在此时的父节点上。'''     siftdown(heap, startpos, pos)  '''up与down一起维持小根堆性质''' def siftdown(heap, startpos, pos):     newitem = heap[pos]     # Follow the path to the root, moving parents down until finding a place     # newitem fits.     while pos > startpos:         parentpos = (pos - 1) >> 1         parent = heap[parentpos]         if newitem < parent:             heap[pos] = parent             pos = parentpos             continue         break     heap[pos] = newitem  def heappop(heap):     """Pop the smallest item off the heap, maintaining the heap invariant."""     lastelt = heap.pop()    # raises appropriate IndexError if heap is empty     print('pop:', lastelt)     if heap:         returnitem = heap[0]         heap[0] = lastelt         siftup(heap, 0)         print('heap:', heap)         print('returnitem', returnitem)         return returnitem     return lastelt  def heapify(x):     """Transform list into a heap, in-place, in O(len(x)) time."""     n = len(x)     for i in reversed(range(n//2)):         '''从最后一个根节点开始,将孩子节点最小的覆盖根节点,         并不断往下找,将较小的孩子提上来。直到没有孩子,将根节点的值覆盖到此时的孩子节点上'''         siftup(x, i)  def heap_sort(arr):     # 将数组转换为小根堆     heapify(arr)     print(arr)      # 弹出堆顶元素直到堆为空     '''每次pop最后一个节点,并输出根节点。     将最后一个节点覆盖在根节点上,并且进行up——down操作位置小根堆性质'''     return [heappop(arr) for _ in range(len(arr))]  if __name__ == '__main__':     # 示例数组     arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]     # 执行堆排序     sorted_arr = heap_sort(arr)     # 打印排序后的数组     print('sorted_arr', sorted_arr)

二、调用python接口

import heapq def heap_sort(arr):     # 将数组转换为小根堆     heapq.heapify(arr)     print(arr)      # 弹出堆顶元素直到堆为空     '''每次pop最后一个节点,并输出根节点。     将最后一个节点覆盖在根节点上,并且进行up——down操作位置小根堆性质'''     return [heapq.heappop(arr) for _ in range(len(arr))]  if __name__ == '__main__':     # 示例数组     arr = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]     # 执行堆排序     sorted_arr = heap_sort(arr)     # 打印排序后的数组     print('sorted_arr', sorted_arr)

输出:

[1, 1, 2, 3, 3, 9, 4, 6, 5, 5, 5]
sorted_arr [1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

三、时间复杂度

O(NlogN)

  1. siftup(heap, pos) 函数:

    • 这个函数将一个元素上浮到它应该在的位置。在最坏的情况下,它可能需要上浮到堆的根节点,时间复杂度是 O(log n),其中 n 是堆中元素的数量。
  2. siftdown(heap, startpos, pos) 函数:

    • 这个函数将一个元素下沉到它应该在的位置。同样,在最坏的情况下,它可能需要下沉到叶子节点,时间复杂度也是 O(log n)。
  3. heappop(heap) 函数:

    • 这个函数移除并返回堆顶元素(最小元素),然后通过调用 siftup 来修复堆。siftup 的时间复杂度是 O(log n),所以 heappop 的时间复杂度也是 O(log n)。
  4. heapify(x) 函数:

    • 这个函数将一个数组转换成一个堆。它从最后一个父节点开始,向上调用 siftup。由于堆的最后一个父节点的索引是 n/2 - 1(n 是数组的长度),所以它实际上调用了大约 n/2 次 siftup。因此,heapify 的时间复杂度是 O(n)。
  5. heap_sort(arr) 函数:

    • 这个函数首先调用 heapify 将数组转换成一个堆,然后通过 n 次调用 heappop 来移除所有元素。由于 heapify 的时间复杂度是 O(n),并且 heappop 的时间复杂度是 O(log n),heap_sort 的总时间复杂度是 O(n log n)。

总结:

  • 时间复杂度:
    • siftup: O(log n)
    • siftdown: O(log n)
    • heappop: O(log n)
    • heapify: O(n)
    • heap_sort: O(n log n)

整体上,对于堆排序算法的时间复杂度分析如下

  • 构建堆(Heapify)heapify 函数将数组转换成一个堆。对于一个长度为 n 的数组,heapify 的时间复杂度是 O(n)。这是通过从最后一个父节点开始,向上调用 siftup 实现的,每个 siftup 操作的时间复杂度是 O(log n),但由于堆的结构特性,实际上 heapify 的总体时间复杂度是线性的。

  • 堆排序(Heap Sort):在 heap_sort 函数中,首先调用 heapify 将数组转换成一个堆,然后通过 n 次调用 heappop 来移除所有元素。每次 heappop 操作的时间复杂度是 O(log n)。因此,n 次 heappop 的总时间复杂度是 O(n log n)。

  • 综合以上两点,堆排序的整体时间复杂度是 O(n + n log n),简化后为 O(n log n)。这是因为在堆排序过程中,构建堆是一次性的,而移除元素需要 n 次操作,每次操作的复杂度是 log n。

  • 空间复杂度:O(1),因为所有操作都是原地进行的。

相关内容

热门资讯

交流学习经验!(WPK透视)透... 交流学习经验!(WPK透视)透视辅助!(透视)外挂辅助修改器(2020已更新)(哔哩哔哩)是一款可以...
终于懂了《wepokE》软件透... 终于懂了《wepokE》软件透明挂!(透明挂)软件规律(2021已更新)(哔哩哔哩);一、有挂的是的...
总算明白微扑克脚本原来确实是有... 您好,微扑克这款游戏可以开挂的,确实是有挂的,需要了解加微【136704302】很多玩家在这款游戏中...
玩家必备教程《wePOKE》软... 玩家必备教程《wePOKE》软件透明挂!(透明挂)软件内置(2020已更新)(哔哩哔哩)玩家必备教程...
最新研发微扑克脚本原来真的有挂... 最新研发微扑克脚本原来真的有挂,太夸张了原来真的有挂,详细教程(有挂方略)大家肯定在之前微扑克或者微...
一分钟揭秘《微扑克辅助器神器》... 您好,微扑克这款游戏可以开挂的,确实是有挂的,需要了解加微【136704302】很多玩家在这款游戏中...
揭秘关于《Wepoke德州局》... 揭秘关于《Wepoke德州局》软件透明挂!(透明挂)软件挂件(2024已更新)(哔哩哔哩);最新版2...
玩家必备科技微扑克智能原来是真... 玩家必备科技微扑克智能原来是真的有挂,太夸张了原来真的是有挂,详细教程(有挂方针);微扑克是一款益智...
攻略讲解!微扑克规律辅助器挂(... 攻略讲解!微扑克规律辅助器挂(辅助挂)原来是有挂(有挂方针)详细教程(哔哩哔哩);1、很好的工具软件...
终于清楚《wPk辅助透视》太坑... 终于清楚《wPk辅助透视》太坑了果然确实真的有挂(有挂猫腻);是一款可以让一直输的玩家,快速成为一个...