当前位置:   article > 正文

用python实现堆_python 堆怎么写

python 堆怎么写

堆是一种数据结构,它是一棵完全二叉树,且某个节点的值总是不大于或不小于其父节点的值;根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。根据完全二叉树的性质,若将堆中的数据至顶向下,从左向右的存在一个一维数组里面,则父节点的位置索引总是该节点位置索引减1再除2取整的结果,如下图所示。

 若直接将一组数如 [8,5,2,9,3,7,1,4,6] 放入上图所示的二叉树(如下图),此时该二叉树还不能称为堆,因为它并不具有数据的有序性(假如我们要实现的是小根堆,每个节点值都要不大于其子节点)。因此必须要采取操作使得其成为堆。

下面介绍几种堆的基本操作

上浮 shift up

小根堆中越小的元素应该越在上面。以上图中6号位置元素1为例,它比它的父节点2小,则它应该和2交换位置,此时1在2号位置。这时1还比它的父节点8小,则它和8交换位置,1在0号位置了。此时1的位置是合理的。这个过程就叫上浮。总结一下就是:

从当前结点开始,和它的父亲节点比较,若是比父亲节点小,就交换,然后将当前询问的节点下标更新为原父亲节点下标;否则结束。 

下沉 shift_down

下沉的目的是让大元素沉在堆的下面。还以上图的子树为例,0位置的8的子节点5和2都比它小,最小的是2,则2和8交换位置,8沉到2号位置,此时它的子节点7和1也都比它小,最小的是1,那就和1交换位置,8沉到了6号位置,结束。总结出下沉操作过程就是让当前结点的左右儿子(如果有的话)作比较,哪个比较小就和它交换,并更新询问节点的下标为被交换的儿子节点下标,否则结束。

 以上面两种基本操作为基础,就很容易实现下面两种操作。

插入 push

在向堆中插入元素时,我们总是将它放在堆的最后的位置,然后将它上浮,这样就能继续维持堆数据的有序性了。

弹出 pop

弹出操作演出的是堆顶的元素,也就是完全二叉树的根节点。若直接弹出根节点,则原来的一棵完全二叉树就变成两棵完全二叉树,这样对继续维护堆造成困难,此时我们将二叉树中最后位置的元素放到根节点位置,这样又是一棵完全二叉树了,然后将现在的根元素下沉就行。

以上是堆的一些操作的基本原理,但在python中实现堆时,操作过程略有不同。为了节省内存,在执行上浮操作时,不是逐次交换位置,而是拿着要上浮的元素去比较,找到合适的位置。下沉操作也是一样。

以下是在python中实现的Heap类。

  1. class Heap:
  2. def __init__(self,elist):
  3. self._elems=list(elist)
  4. if elist:
  5. self.buildheap()
  6. def is_empty(self):
  7. return not self._elems
  8. #取堆顶元素
  9. def peek(self):
  10. if self.is_empty():
  11. raise ValueError("堆为空")
  12. return self._elems[0]
  13. #上浮
  14. def siftup(self,e,last):
  15. elems,i,j=self._elems,last,(last-1)//2
  16. while i>0 and e<elems[j]:
  17. elems[i]=elems[j]
  18. i,j=j,(j-1)//2
  19. elems[i]=e
  20. #插入
  21. def push(self,e):
  22. self._elems.append(None)
  23. self.siftup(e,len(self._elems)-1)
  24. #下沉
  25. def siftdown(self,e,begin,end):
  26. elems,i,j=self._elems,begin,begin*2+1
  27. while j<end:
  28. if j+1<end and elems[j+1]<elems[j]:
  29. j+=1
  30. if e<elems[j]:
  31. break
  32. elems[i]=elems[j]
  33. i=j
  34. j=2*j+1
  35. elems[i]=e
  36. #弹出
  37. def pop(self):
  38. if self.is_empty():
  39. raise ValueError("堆为空")
  40. elems=self._elems
  41. e0=elems[0]
  42. e=elems.pop()
  43. if len(elems)>0:
  44. self.siftdown(e,0,len(elems))
  45. return e0
  46. #从数组构建堆
  47. def buildheap(self):
  48. end=len(self._elems)
  49. for i in range(end//2-1,-1,-1):
  50. self.siftdown(self._elems[i],i,end)

在堆的初始化函数中,传入的是一个list,函数中使用的是它的拷贝,这是避免直接引用该list对象会造成的麻烦。然后直接从list创建一个堆,采用的是逐个元素下沉的方式,并不断扩大下沉范围的上限i。注意i是从end//2-1开始,这是因为只有从下标end//2-1开始向上位置的元素才有子节点。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/516777
推荐阅读
相关标签
  

闽ICP备14008679号