优先队列及堆排序
堆排序( Heap Sort
)由威尔士-加拿大计算机科学家 J. W. J. Williams
在 1964
年发明,它利用了二叉堆 (A binary heap)
的性质实现了排序,并证明了二叉堆数据结构的可用性。同年,美国籍计算机科学家 R. W. Floyd
在其树排序研究的基础上,发布了一个改进的更好的原地排序的堆排序版本。
堆排序属于选择类排序算法。
一、优先队列
优先队列是一种能完成以下任务的队列:插入一个数值,取出最小或最大的数值(获取数值,并且删除)。
优先队列可以用二叉树来实现,我们称这种结构为二叉堆。
最小堆和最大堆是二叉堆的一种,是一颗完全二叉树(一种平衡树)。
最小堆的性质:
- 父节点的值都小于左右儿子节点。
- 这是一个递归的性质。
最大堆的性质:
- 父节点的值都大于左右儿子节点。
- 这是一个递归的性质。
最大堆和最小堆实现方式一样,只不过根节点一个是最大的,一个是最小的。
1.1. 最大堆特征
最大堆实现细节(两个操作):
- push:向堆中插入数据时,首先在堆的末尾插入数据,如果该数据比父亲节点还大,那么交换,然后不断向上提升,直到没有大小颠倒为止。
- pop:从堆中删除最大值时,首先把最后一个值复制到根节点上,并且删除最后一个数值,然后和儿子节点比较,如果值小于儿子,与儿子节点交换,然后不断向下交换, 直到没有大小颠倒为止。在向下交换过程中,如果有两个子儿子都大于自己,就选择较大的。
最大堆有两个核心操作,一个是上浮,一个是下沉,分别对应 push
和 pop
。
这是一个最大堆:
用数组表示为:[11 5 8 3 4]
1.2. 上浮操作
我们要往堆里 push
一个元素 15
,我们先把 X = 15
放到树最尾部,然后进行上浮操作。
因为 15
大于其父亲节点 8
,所以与父亲替换:
这时 15
还是大于其父亲节点 11
,继续替换:
操作一次 push
的最好时间复杂度为:O(1)
,因为第一次上浮时如果不大于父亲,那么就结束了。最坏的时间复杂度为: O(logn)
,相当于每次都大于父亲,会一直往上浮到根节点,翻转次数等于树的高度,而树的高度等于元素个数的对数:log(n)
。
1.3. 下沉操作
我们现在要将堆顶的元素 pop
出。如图我们要移除最大的元素 11
: