赞
踩
堆是一种数据结构,它是一棵完全二叉树,且某个节点的值总是不大于或不小于其父节点的值;根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。根据完全二叉树的性质,若将堆中的数据至顶向下,从左向右的存在一个一维数组里面,则父节点的位置索引总是该节点位置索引减1再除2取整的结果,如下图所示。
若直接将一组数如 [8,5,2,9,3,7,1,4,6] 放入上图所示的二叉树(如下图),此时该二叉树还不能称为堆,因为它并不具有数据的有序性(假如我们要实现的是小根堆,每个节点值都要不大于其子节点)。因此必须要采取操作使得其成为堆。
下面介绍几种堆的基本操作
小根堆中越小的元素应该越在上面。以上图中6号位置元素1为例,它比它的父节点2小,则它应该和2交换位置,此时1在2号位置。这时1还比它的父节点8小,则它和8交换位置,1在0号位置了。此时1的位置是合理的。这个过程就叫上浮。总结一下就是:
从当前结点开始,和它的父亲节点比较,若是比父亲节点小,就交换,然后将当前询问的节点下标更新为原父亲节点下标;否则结束。
下沉的目的是让大元素沉在堆的下面。还以上图的子树为例,0位置的8的子节点5和2都比它小,最小的是2,则2和8交换位置,8沉到2号位置,此时它的子节点7和1也都比它小,最小的是1,那就和1交换位置,8沉到了6号位置,结束。总结出下沉操作过程就是让当前结点的左右儿子(如果有的话)作比较,哪个比较小就和它交换,并更新询问节点的下标为被交换的儿子节点下标,否则结束。
以上面两种基本操作为基础,就很容易实现下面两种操作。
在向堆中插入元素时,我们总是将它放在堆的最后的位置,然后将它上浮,这样就能继续维持堆数据的有序性了。
弹出操作演出的是堆顶的元素,也就是完全二叉树的根节点。若直接弹出根节点,则原来的一棵完全二叉树就变成两棵完全二叉树,这样对继续维护堆造成困难,此时我们将二叉树中最后位置的元素放到根节点位置,这样又是一棵完全二叉树了,然后将现在的根元素下沉就行。
以上是堆的一些操作的基本原理,但在python中实现堆时,操作过程略有不同。为了节省内存,在执行上浮操作时,不是逐次交换位置,而是拿着要上浮的元素去比较,找到合适的位置。下沉操作也是一样。
以下是在python中实现的Heap类。
- class Heap:
- def __init__(self,elist):
- self._elems=list(elist)
- if elist:
- self.buildheap()
-
- def is_empty(self):
- return not self._elems
-
- #取堆顶元素
- def peek(self):
- if self.is_empty():
- raise ValueError("堆为空")
- return self._elems[0]
-
- #上浮
- def siftup(self,e,last):
- elems,i,j=self._elems,last,(last-1)//2
- while i>0 and e<elems[j]:
- elems[i]=elems[j]
- i,j=j,(j-1)//2
- elems[i]=e
-
- #插入
- def push(self,e):
- self._elems.append(None)
- self.siftup(e,len(self._elems)-1)
-
- #下沉
- def siftdown(self,e,begin,end):
- elems,i,j=self._elems,begin,begin*2+1
- while j<end:
- if j+1<end and elems[j+1]<elems[j]:
- j+=1
- if e<elems[j]:
- break
- elems[i]=elems[j]
- i=j
- j=2*j+1
- elems[i]=e
-
- #弹出
- def pop(self):
- if self.is_empty():
- raise ValueError("堆为空")
- elems=self._elems
- e0=elems[0]
- e=elems.pop()
- if len(elems)>0:
- self.siftdown(e,0,len(elems))
- return e0
-
- #从数组构建堆
- def buildheap(self):
- end=len(self._elems)
- for i in range(end//2-1,-1,-1):
- self.siftdown(self._elems[i],i,end)
在堆的初始化函数中,传入的是一个list,函数中使用的是它的拷贝,这是避免直接引用该list对象会造成的麻烦。然后直接从list创建一个堆,采用的是逐个元素下沉的方式,并不断扩大下沉范围的上限i。注意i是从end//2-1开始,这是因为只有从下标end//2-1开始向上位置的元素才有子节点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。