1、算法思想
先回顧以下堆的概念
堆分爲兩種:最大堆和最小堆,兩者的差別在於節點的排序方式。
在最大堆中,父節點的值比每一個子節點的值都要大。在最小堆中,父節點的值比每一個子節點的值都要小。這就是所謂的“堆屬性”,並且這個屬性對堆中的每一個節點都成立。
例子:
這是一個最大堆,因爲每一個父節點的值都比其子節點要大。10 比 7 和 2 都大。7 比 5 和 1都大。
堆的實際存儲結構是一個數組,i表示根節點的索引,i2+1和i2+2分別表示根節點i的左右子節點。
上圖所示的堆實際存儲爲
[10,7,2,5,1]
堆排序就是先將數組中元素構建成一個最大堆,然後把堆頂的元素與最後一個元素交換,交換之後破壞了堆的特性,我們再把堆中剩餘的元素再次構成一個大頂堆,然後再把堆頂元素與最後第二個元素交換….如此往復下去,等到剩餘的元素只有一個的時候,此時的數組就是有序的了。
爲方便理解我還準備了動圖:
2、實現代碼
// 堆排序
void headSort(int* arr int length) {
//構建大頂堆
for (int parent = (length - 2) / 2; parent >= 0; parent--)
{
downAdjust(arr, parent, length - 1);
}
//進行堆排序
for (int i = length - 1; i >= 1; i--) {
// 把堆頂元素與最後一個元素交換
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
// 把打亂的堆進行調整,恢復堆的特性
downAdjust(arr, 0, i - 1);
}
}
//下沉操作
void downAdjust(int* arr, int parent, int length) {
//臨時保存要下沉的元素
int temp = arr[parent];
//定位左孩子節點的位置
int child = 2 * parent + 1;
//開始下沉
while (child <= length) {
// 如果右孩子節點比左孩子大,則定位到右孩子
if(child + 1 <= length && arr[child] < arr[child + 1])
child++;
// 如果孩子節點小於或等於父節點,則下沉結束
if (arr[child] <= temp ) break;
// 父節點進行下沉
arr[parent] = arr[child];
parent = child;
child = 2 * parent + 1;
}
arr[parent] = temp;
}
}
3、算法分析
- 時間複雜度:O(nlogn)
- 空間複雜度:O(1)
- 非穩定排序
- 原地排序