思想:
堆排序利用了优先队列的数据结构,其排序方法是将所有数据放入优先队列中,然后依次从堆顶取出元素,放在数组中,完成排序。因此,其核心是优先队列的实现。
优先队列的实现是依靠二叉堆进行的。二叉堆的性质是其两个子节点都要小于等于父节点(不分左右)。对应的有上浮和下沉两个操作。
上浮:当元素比父节点大时交换二者,直至满足元素小于父节点。
下沉:将元素的左右子节点比较,并把元素与较大子节点交换,依次进行,直至元素大于其子节点或元素达到堆底。
复杂度:
时间复杂度: O(nlgn) O ( n l g n )
空间复杂度: O(n) O ( n )
特点:
- 最坏情况为: O(nlgn) O ( n l g n ) ,但是平均效率不如快排,原因是大量的上浮和下沉操作带来的过多的交换操作。使得元素在每次排序后在较大范围内变动。
- 非稳定排序
伪代码:
利用数组构造完全二叉堆,因为数组的效率比链表高,支持随机访问。数组构造完全二叉堆时节点k的子节点为2*k,2*k+1。
上浮:
swim(arr[], k, N){//N:数组总数
while(k>1 && arr[k]<arr[k/2]){
exchange(arr, k, k/2);
k=k/2;
}
}
下沉:
sink(arr[], k, N){
while(2*k<=N){
j=2*k;
if(arr[j]<arr[j+1]) j++;
if(arr[k]<arr[j]) break;
exchange(arr[], k, j);
k = j;
}
}
堆排序:
sort(arr[]){
for(i=N/2->1){//构造堆用下沉操作可以比上浮操作快一倍,少比较了N/2的数
sink(arr[], i, N);
}
while(N>1){
exchange(arr[], 1, N--);//N--保证了排好序的元素不会改变位置。
sink(arr[], 1, N);
}
}
优化
- 堆的构造从下至上可以比从上至下节省一半元素操作。