堆排序有两个侧重点,一个是 “堆”,一个是“排序”
1、“堆”——建小堆
2、“排序”——将堆顶元素和堆底的最后一个元素交换,除了堆底最后一个元素外,调整剩下的元素
建小堆在前一篇博客中已经完成了
接下来将在前面的基础上 进行排序
目录
一、提出问题
现在有一个 小堆数组,在不额外开辟空间的情况下,得到一个从小到大排列的有序数组
二、思路分析
1、画二叉树
我们将数组画成二叉树的形式来分析
2、将堆顶元素和堆底的最后一个元素互换
这样做的目的是,将已经得到的最小数放到最后,除了最小数外,剩下的数重新调整
绿色圈中,根元素的左子树 和 右子树 都是标准的小堆,所以我们没有必要像 建堆 一样,从底部的子树开始调整,我们只需要从根部向下调整即可
3、准备下一轮调整
end前移,树的节点数size要减少一个,因为end 后面的节点都是每一轮选出来的最小数
三、代码实现
我们以最初的为例,
(1)先把第一个元素和最后一个元素交换
(2)然后需要调整的节点个数size 减一
(3)从根部向下调整
(4)树的尾部 end 向前移动,end 减一
void HeapSort(int* a,int size)
{
CreateHeap(a, size);
int end = size - 1;
while (end>0)
{
//将堆顶元素和堆底元素互换
Swap(a[0], a[end]);
//交换以后,最小的数放在堆底,要参加堆调整的节点个数要减一
size--;
//由于子树都是小堆,直接从堆顶向下调整
AdjustDown(a, 0, size);
//每得到一个最小的,都会被放到最后,最后一个节点的位置向前移动
end--;
}
}
四、测试
int main() {
int arr[] = { 70,56,30,24,33,10,75,68,90,21};
HeapSort(arr, sizeof(arr) / sizeof(arr[0]));
for (size_t i = sizeof(arr) / sizeof(arr[0])-1; i > 0 ; i--)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
五、时间复杂度
建小堆的时间复杂度:O(N)
每次从根部调整的时间复杂度:O(log N)
需要调整 N-K次
所以时间复杂度是 O(N)+O((N-1)*log N) ≈ O(Nlog N)