学习了堆排序,使用python实现了一个优先队列结构,记录一下实现过程:
用一个python的list来表示堆结构,将list作为参数传入构造函数中,然后在构造函数中建堆:
class prioQueue: def __init__(self, elist=[]): self._elems = list(elist) if elist: self.buildheap()
堆一般都是一个完全二叉树,那么根据完全二叉树的性质,一个节点i的左子节点为i+1,右子节点为i*2+1,以最小堆为例,根节点一定是最小值,优先队列必须保证每次弹出的值都是最小的。建堆过程是一个递归的过程,首先拿出list中第一个元素,一个元素必然是一个堆,接下来从list最后拿2个元素分别作为2个子堆,若要将第一个元素这个堆和最后2个元素所构成的子堆合并成一个堆,那么就要保证堆顶的值最小,先判断左右2个子堆的大小,然后用第一个元素和较小的比较,若小,则不变,若大,则将元素与子堆那个元素调换位置,这样就形成了1个3个元素的最小堆,之后再从list中拿出第二个元素,从排序好的堆中一次进行这个步骤,若较小,则跳出循环拿出下一个元素,若较大则交换2个比较的元素,再用这个元素与2个子节点的元素进行比较,直到遍历了整个二叉树都没有更大的元素,那么这个元素就将放在元素的最末尾。这便是一个从堆顶即二叉树根节点开始向下扫描的过程,先定义这个siftDown方法:
def siftdown(self, e,begin, end): elems, i, j, = self._elems, begin , begin*2+1
e为要排序的元素,变量i 储存我们开始排序的位置,为了不浪费空间,begin参数来作为排序好及位排好的元素list中的索引,所以一开始是0之后递增,end参数代表着堆元素的个数,也就是len(list)。
然后我们用elems变量保存数组 i作为排序元素的指针 j为i的左子树 进行循环。只要j没有到最后就循环下去。
while j < end:
然后先比较左右子树,使用较小的子树来和插入的e做比较,若e小于它那么就找到位置,否则调换位置继续与换位置后的左右子树进行比较,i位置即是元素e所在的位置:
if j+1<end and elems[j+1] < elems[j]: j = j + 1 if e < elems[j]: break elems[i] = elems[j] i, j = j , 2*j+1 elems[i] = e
对list的每一个元素都进行这样的一个向下扫描,就可以完成一个建堆的过程,得到一个最小堆:
def buildheap(self): end = len(self._elems) for i in range(end//2,-1,-1): self.siftdown(self._elems[i],i,end) return self._elems