堆排序
堆排是基于堆的一种排序算法,对于堆的了解请点开链接:https://blog.csdn.net/z_x_m_m_q/article/details/82320357(创建、插入、删除)
算法思想:
之前的博客写了小堆,由于降序排列是根据小堆性质进行排列的,顺着前面的知识这里我们讨论从大到小的排列
首先小堆建好之后堆顶数据是堆中最小的数据,将堆顶元素与最后的一个元素交换位置,这样最后元素就出在有序区间了。再对除有序区间外的其他数据从堆顶开始来一次向下调整,这样堆顶数据又是堆中最小的数据,重复上述步骤直至堆中只剩下一个元素。
以具有N个数据的数组 a[N] 为例,说下对排序的步骤:
- 利用数组中元素建立节点量为 N 的小堆
- 第一次将a[0] 与 a[N-1]交换位置,对a[0]至a[N-2]进行一次向下调整,重新恢复成小堆(此时堆的节点量为N-1)
- 第二次将a[0] 与 a[N-2]交换位置,对a[0] 至 a[N-3]进行一次向下调整,重新恢复成小堆(此时堆的节点量变为n-2)
- 重复这样的操作,直至 a[0] 与 a[1]交换
图形分析:
这样一直下去,就好了。。。
算法代码:
这里我只写了核心代码:
void HeapSort(int* a,int n)
{
int i = 0;
//建小堆
for (i = (n-2)/2;i >= 0;--i)
HeapAdjustDown(a, n, i);
//交换堆顶、堆尾数据
for (i = n-1;i > 0;--i)
{
Swap(&a[0],&a[i]);
HeapAdjustDown(a, i, 0); //每次从堆顶开始向下调整,i 刚好是新堆的节点数
}
}
时间复杂度和空间复杂度:
时间复杂度:
想想堆排的整个过程,第一步建堆,第二步执行 N-1 次交换及向下调整,最后取两者复杂度较高的就行了
建堆时间复杂度:
建堆的时候时间消耗在下沉操作上,而下沉操作最多下沉到底,显然,高度为h的节点下沉代价为O(h)
堆中所有元素下沉代价之和就是建堆的时间复杂度
把堆中不同高度中的所有节点相加起来就是全部的节点
所以问题变为:
- 高度最高多高(高度上界)
- 高度h有多少节点
然后我们把不同高度的每个节点执行下沉所需要的代价累加起来
最终记住建堆复杂度为O(n)就行了
执行 N-1 次交换及向下调整时间复杂度:
Swap 操作代价为常数,相对于下沉操作的代价可以忽略不计
那么执行 N-1 次向下调整的时间复杂度为
两个步骤相加的复杂度为:O(n)+O(nlgn),O(nlgn)复杂度高于O(n),所以堆排序的时间复杂度为O(nlgn)
提示:上面的过程有些复杂,我也是参考别人的。通过以上过程理解算法的时间复杂度是一个很棒的机会。
空间复杂度:
堆排序通过简单的交换就能把数据排成有序的,堆和原数组是一体的,不需要辅助空间,所以空间复杂度为
稳定性:
不是稳定的