堆排序是利用堆的性質進行的一種選擇排序。下面先討論一下堆。
1.堆
堆實際上是一棵完全二叉樹,其任何一非葉節點滿足性質:
Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2]
即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。
堆分爲大頂堆和小頂堆,滿足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱爲大頂堆,滿足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱爲小頂堆。由上述性質可知大頂堆的堆頂的關鍵字肯定是所有關鍵字中最大的,小頂堆的堆頂的關鍵字是所有關鍵字中最小的。
2.堆排序的思想
利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。
其基本思想爲(大頂堆):
1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無須區;
2)將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序區(R1,R2,......Rn-1)和新的有序區(Rn),且滿足R[1,2...n-1]<=R[n];
3)由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整爲新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。
操作過程如下:
1)初始化堆:將R[1..n]構造爲堆;
2)將當前無序區的堆頂元素R[1]同該區間的最後一個記錄交換,然後將新的無序區調整爲新的堆。
因此對於堆排序,最重要的兩個操作就是構造初始堆和調整堆,其實構造初始堆事實上也是調整堆的過程,只不過構造初始堆是對所有的非葉節點都進行調整。
下面舉例說明:
給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。
首先根據該數組元素構建一個完全二叉樹,得到
20和16交換後導致16不滿足堆的性質,因此需重新調整
這樣就得到了初始堆。
此時3位於堆頂不滿堆的性質,則需調整繼續調整
#include "iostream"
#include "algorithm"
using namespace std;
void HeapSort(int *a,int size);
void BuildHeap(int *a,int size);
void HeapAdjust(int *a,int i,int size);
void main()
{
int size;
cin>>size;
int *a=new int[size];
for (int i=0;i<size;i++)
cin>>a[i];
HeapSort(a,size);
for(int i=0;i<size;i++)
cout<<a[i]<<" ";
cout<<endl;
delete []a;
return;
}
void HeapSort(int *a,int size)
{
BuildHeap(a,size);
for(int i=size-1;i>=0;i--)
{
swap(a[0],a[i]);
HeapAdjust(a,0,i);
}
}
void BuildHeap(int *a,int size)
{
for(int i=size/2-1;i>=0;i--)
HeapAdjust(a,i,size);
}
void HeapAdjust(int *a,int i,int size)
{
int leftnode=2*i+1;
int rightnode=2*i+2;
int max=i;
if (i<=size/2-1)
{
if (leftnode<size && a[leftnode]>a[max])
max=leftnode;
if (rightnode<size && a[rightnode]>a[max])
max=rightnode;
if (max!=i)
{
swap(a[i],a[max]);
HeapAdjust(a,max,size);
}
}
}