堆排序是对选择排序的一种优化算法,都属于选择排序类。
基本过程:堆是一种完全二叉树,是一种树形选择结构,将待排序的序列构造成一个大顶堆(即每个节点的值都大于或等于其左右孩子节点的值),然后将堆顶的根节点移走,与堆数组的末尾元素交换,此时末尾元素就是最大值。再将剩余的n-1个序列重新构造一个堆,得到次大值置于末尾,反复执行得到一个有序序列。
过程可参考如下图:
这里需要注意的,堆排序为了方便完全二叉树的计数,序列的首位置为空,忽略不计。
示例代码如下:
def heap_sort(lst): n = len(lst)-1 # 这里注意根据python的range列表的性质,要将序列长度减一,否则列表长度溢出 for i in range(n/2, 0, -1): heap_adjust(lst, i, len(lst)) while n > 1: lst[1], lst[n] = lst[n], lst[1] # 将堆顶记录和当前未经排序子序列的最后一个记录交换 heap_adjust(lst, 1, n) # 将lst[n]重新调整为大顶堆 n -= 1 return lst[1:] # 这里为了方便完全二叉树的计数,将序列中的首个元素置为空忽略不计 def heap_adjust(lst, k, size): # 自顶向下堆化,从k开始堆化 N = size - 1 while 2 * k <= N: lchild = 2 * k rchild = 2 * k + 1 if lchild < N and lst[lchild] < lst[rchild]: # 选出左右孩子节点中更大的那个,并将关键字赋值给左孩子 lchild = rchild if lst[k] < lst[lchild]: # 将左孩子与父节点比较,选出大的 lst[k], lst[lchild] = lst[lchild], lst[k] k = lchild # 将k扩倍,对孙节点排序 else: break if __name__ == "__main__": start = time.clock() rand_lst = [] for i in range(10): rand_lst.append(round(random.random()*100, 2)) lst = heap_sort(rand_lst) end = time.clock() print lst print "done ", (end-start)
性能方面:
堆排序的运行时间主要消耗在初始构建堆和重建堆的反复筛选上。
由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度都为O(nlogn),空间复杂度上它只有一个用来交换的暂存单元,所以为O(1),但是由于记录的比较和交换是跳跃进行的,所以堆排序是不稳定的排序方法。此外,由于它的性能主要消耗在堆的创建与重建上,所以不适合排序序列少的情况。