首先介紹堆的定義:堆是具有下列性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆。由於堆的表象結構是一棵完全二叉樹,我們稱之爲二叉堆。下面分別就是兩個大頂堆和小頂堆:
隨後我們都是以大頂堆爲例來介紹堆排序的,小頂堆也是一樣的。
對於大頂堆,我們直到,根結點肯定是整個堆中最大的結點,因此我們如果每次都取出根結點,然後再將堆的剩餘元素重新調整成大頂堆,再取根結點,即可以獲得一個有序序列。但是這裏要考慮到幾個問題:
1、怎麼存儲這個大頂堆?
2、如何初始化一個大頂堆?
3、每次取出根結點後該如何調整成一個新的大頂堆?
大頂堆的存儲
每次取出根結點後該如何調整成一個新的大頂堆
void ElemDown(int *array, int index, int arrayCount)
{
while(2 * index + 1 < arrayCount - 1)
{
if(2 * index + 2 < arrayCount - 1) //右孩子也存在
{
if(array[index] >= array[2 * index + 1] && array[index] >= array[2 * index + 2]) //比兩個孩子都大
break;
int bigIndex = array[2 * index + 1] >= array[2 * index + 2]?2 * index + 1:2 * index + 2; //取較大孩子下標
int temp = array[bigIndex]; //互換元素
array[bigIndex] = array[index];
array[index] = temp;
index = bigIndex; //繼續下沉
}
else //只存在左孩子,則左孩子肯定是葉子結點
{
if(array[index] < array[2 * index + 1]) //比左孩子小則互換
{
int temp = array[index];
array[index] = array[2 * index + 1];
array[2 * index + 1] = temp;
}
break; //都是要結束了
}
}
}
初始化一個大頂堆
void MakeBigHeap(int *array, int arrayCount)
{
int fatherNo = arrayCount/2 - 1; //每顆子樹的父結點下標
for(; fatherNo >= 0; --fatherNo)
ElemDown(array, fatherNo, arrayCount);
}
好了,現在可以對其進行排序了。對於給定的一個亂序數組,第一步將其初始化爲一個大頂堆,然後每次將根結點跟最後一個結點互換,然後在對這個新的(除去最後一個元素)的樹進行“下沉”操作(因爲此時只有根結點是不在原位的,其左右子樹都是大頂堆)。最後所得到的就是一個升序的序列了。代碼如下:
void HeapSort(int array[], int arrayCount)
{
MakeBigHeap(array, arrayCount); //構造大頂堆
int temp;
for(int newArrayCount = arrayCount; newArrayCount > 0; --newArrayCount) //去除根結點
{
temp = array[0];
array[0] = array[newArrayCount - 1];
array[newArrayCount - 1] = temp;
ElemDown(array, 0, newArrayCount);
}
}
#include <iostream>
using namespace std;
void ElemDown(int *array, int index, int arrayCount)
{
while(2 * index + 1 < arrayCount - 1)
{
if(2 * index + 2 < arrayCount - 1) //右孩子也存在
{
if(array[index] >= array[2 * index + 1] && array[index] >= array[2 * index + 2]) //比兩個孩子都大
break;
int bigIndex = array[2 * index + 1] >= array[2 * index + 2]?2 * index + 1:2 * index + 2; //取較大孩子下標
int temp = array[bigIndex]; //互換元素
array[bigIndex] = array[index];
array[index] = temp;
index = bigIndex; //繼續下沉
}
else //只存在左孩子,則左孩子肯定是葉子結點
{
if(array[index] < array[2 * index + 1]) //比左孩子小則互換
{
int temp = array[index];
array[index] = array[2 * index + 1];
array[2 * index + 1] = temp;
}
break; //都是要結束了
}
}
}
void MakeBigHeap(int *array, int arrayCount)
{
int fatherNo = arrayCount/2 - 1;
for(; fatherNo >= 0; --fatherNo)
ElemDown(array, fatherNo, arrayCount);
}
void HeapSort(int array[], int arrayCount)
{
cout<<"before sort: \n";
for(int index = 0; index < arrayCount; ++index)
cout<<array[index]<<" ";
MakeBigHeap(array, arrayCount); //構造大頂堆
cout<<"\nafter make big sort: \n";
for(int index = 0; index < arrayCount; ++index)
cout<<array[index]<<" ";
int temp;
for(int newArrayCount = arrayCount; newArrayCount > 0; --newArrayCount)
{
temp = array[0];
array[0] = array[newArrayCount - 1];
array[newArrayCount - 1] = temp;
ElemDown(array, 0, newArrayCount);
}
cout<<"\nafter sort: \n";
for(int index = 0; index < arrayCount; ++index)
cout<<array[index]<<" ";
}
void main()
{
int array[] = {9, 1, 5, 8, 3, 7, 4, 6, 2};
HeapSort(array, 9);
system("pause");
}
運行結果如下: