* 堆排序
時間複雜度:O(nlogn)
空間複雜度:O(1)
屬於不穩定排序算法。
(1)用大根堆排序的基本思想
① 先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區
② 再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,
由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
③ 由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整爲堆。
然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,
由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n- 2].keys≤R[n-1..n].keys,
同樣要將R[1..n-2]調整爲堆。
……
直到無序區只有一個元素爲止。
(2)大根堆排序算法的基本操作:
① 初始化操作:將R[1..n]構造爲初始堆;
② 每一趟排序的基本操作:將當前無序區的堆頂記錄R[1]和該區間的最後一個記錄交換,然後將新的無序區調整爲堆(亦稱重建堆)。
注意:
①只需做n-1趟排序,選出較大的n-1個關鍵字即可以使得文件遞增有序。
②用小根堆排序與利用大根堆類似,只不過其排序結果是遞減有序的。
堆排序和直接選擇排序相反:在任何時刻,堆排序中無序區總是在有序區之前,
且有序區是在原向量的尾部由後往前逐步擴大至整個向量爲止。
*/
/*
* eg.數組{16, 7, 3, 20, 17, 8}
16
/ \
7 3
/ \ /
20 17 8
1.調整爲最大堆
20
/ \
17 8
/ \ /
7 16 3
2.首元素[3]和尾元素[20]交換
3
/ \
17 8
/ \ /
7 16 20
將前5個元素(元素20除外)調整爲最大堆
17
/ \
16 8
/ \ /
7 3 20
3.首元素[17]和倒數第2個元素[3]交換
3
/ \
16 8
/ \ /
7 17 20
將前4個元素(20和17除外)調整爲最大堆
16
/ \
7 8
/ \ /
3 17 20
4.首元素[16]和倒數第3個元素[3]交換
3
/ \
7 8
/ \ /
16 17 20
5.首元素[3]和倒數第4個元素[8]交換
8
/ \
7 3
/ \ /
16 17 20
將前3個元素調整爲最大堆
3
/ \
7 8
/ \ /
16 17 20
6.首元素[3]和倒數第5個元素[7]交換
7
/ \
3 8
/ \ /
16 17 20
將前兩個元素調整爲最大堆
3
/ \
7 8
/ \ /
16 17 20
*/
#include <iostream>
using namespace std;
/*
* 調整爲最大堆
*/
void HeapAdjust(int *arr, int index, int len)
{
while(2*index+1 < len){
//左孩子索引
int childIndex = 2*index+1;
if(2*index+2 < len){
if(arr[2*index+1] < arr[2*index+2]){
//右孩子索引
childIndex = 2*index + 2;
}
}
if (arr[index] < arr[childIndex])
{
int tmp = arr[index];
arr[index] = arr[childIndex];
arr[childIndex] = tmp;
index = childIndex;
}
else{
break;
}
}
}
void HeapSort(int *arr, int len)
{
int i;
//將整個數組按最大堆排序
for(i=len/2-1; i>=0; i--)
{
// len/2-1是最後一個非葉節點
HeapAdjust(arr, i, len);
}
//將arr[0..i)按最大堆排序
for (i = len-1; i>0; i--)
{
//arr[0]是最大的一個元素,將其與arr[i]交換,使大元素排到數組尾部
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
//交換後破壞了最大堆,arr[0..i)重新按最大堆排序
HeapAdjust(arr, 0, i);
}
}
int main()
{
int array[] = {16, 7, 3, 20, 17, 8};
HeapSort(array, 6);
for(int i=0; i<6; i++)
cout<<array[i]<<" ";
cout<<endl;
return 0;
}