最簡單,最直接的講述堆排序
一、什麼是堆,何來堆排序呢?
堆是一顆完全二叉樹,且父節點必須大於子節點。什麼叫完全二叉樹呢,完全二叉樹指的是前面n個節點都是滿二叉樹中的節點換句話說就是完全二叉樹的子節點必須處於樹的最後兩行,且是從左到右的順序。
二、堆排序的基本思想
通過對堆的調整得到根節點,因爲調整後的根節點是整棵樹最大的,這樣就得到了此時的最大值,這時將這個根節點下標識數組第一個數,所以將其與最後一個數交換位置,這樣,得到了第一個最大值,以此類推,可以得到一列數的順序。這樣得到的是最大推,當然也存在最小堆了,道理是一樣的,只是父節點都小於子節點罷了。很多書上寫的都是最大堆,我來寫最小堆吧。
1、 在這裏我們可以看到以數組爲10 的例子來講,首先這是一顆完全二叉樹,但還不是堆,此時我們需要滿足對的條件,那麼我們需要子節點小於父節點那麼這樣纔是一個堆。
2、我們通過對這個原理掃描一趟可以看到所有的父節點大於子節點,這樣會出現一個問題,是什麼問題呢,我們會驚人的發現在根節點的數大於任何一個節點的數這樣我們就得到了這個數組中的最大值。按此結論我們可以通過10次循環可以不斷的得到倒數第二大的數,倒數第三大的數以此類推,可以得到這個數組的順序。
3、這裏我們先需要解決一個問題,構建堆,第一次掃描我們知道了最大值,我們對剩餘的數再建堆就可以得到第二大的數,我們可以放與數組的最後一這位交換位置,直到所有數都排序完畢。這個堆必須從底層向上掃描,因爲我們知道做節點left = 2*i+1,右節點right = 2*I+2這樣的話,必須從數組的最後向前掃描。我們通過遞歸調用我們的建堆就可以得到所有的父節點大於子節點直到根節點爲當前的最大數。
4、這裏最關見的一個問題是:我們必須在一個父節點,兩個子節點中得出最大的值並交換位置,這樣我們做遞歸調用就可以實現對整棵樹的掃描。
關鍵步驟:
第一步是要得到一個子節點與父節點中的最小的然後交換得到父節點爲此節點中最小的。
代碼:
//創建堆的交換操作實現調整
/**
*@param int a[]表示數組
*@param int pos 表示比較的位置,可理解爲父節點的下標
*@param int max 表示數組長度
*@return 無
*/
int heapAdjust(int a[],int pos,int maxLength){
int left = 2*pos+1;//左節點的下標
int right = 2*pos+2;//右節點的下標
int p = pos ;//表示指向最小值的“指針”用來記錄最小值的下標
if(pos <maxLength&&pos>=0){
if(left<maxLength&&a[p]>=a[left]){
p = left;
}
if(right<maxLength&&a[p]>=a[right]){
p = right;
}
if(p != pos ){
int temp= 0;
temp = a[p];
a[p] = a[pos];
a[pos] = temp;
heapAdjust(a,p,maxLength);
}
}else{
cout<<"參數pos不合法!"<<endl;
return 0;
}
return a[0];
}
第二步是要重複調用得到根節點爲此一列數中最小的值
代碼:
//獲取每一次排列時最小的數
/**
*@param int a[] 用來接收要排序的數組
*@param int maxLength 表示數組的長度
*@return int
*/
int GetHeap(int a[],int maxLength){
for(int i = maxLength-1;i>=0;i--){
heapAdjust(a,i,maxLength);
}
cout<<"---------------------------每一次找到的最小值:---------------------------"<<a[0]<<endl;
return a[0];
}
第三步是堆的排序,將得到的根節點中的最小值放入對應的數組中。
代碼:
//用來排序的將每一次得到的最小值進行排序
/**
*@param int a[] 用來接收要排序的數組
*@param int maxLength 表示數組的長度
*@return int
*/
void heap_Sort(int a[],int maxLength){
int temp = 0;
int count = 0;
for(int i = maxLength-1;i>=0;i--){
GetHeap(a,i+1);//此處i+1是爲了保持數組的長度不變
temp = a[0];
a[0]=a[i];
a[i] =temp;
count++;
cout<<"count="<<count<<endl;
}
}
最後是輸出:
這裏強調的是:子節點:left = 2i +1,right = 2i+2 父節點:i
全部實現代碼:
/**
*堆排序的實現
*@author 菜鳥
*@version 2014.6.12
*/
#include <iostream>
#include <windows.h>
using namespace std;
//創建堆的交換操作實現調整
/**
*@param int a[]表示數組
*@param int pos 表示比較的位置,可理解爲父節點的下標
*@param int max 表示數組長度
*@return 無
*/
int heapAdjust(int a[],int pos,int maxLength){
int left = 2*pos+1;//左節點的下標
int right = 2*pos+2;//右節點的下標
int p = pos ;//表示指向最小值的“指針”用來記錄最小值的下標
if(pos <maxLength&&pos>=0){
if(left<maxLength&&a[p]>=a[left]){
p = left;
}
if(right<maxLength&&a[p]>=a[right]){
p = right;
}
if(p != pos ){
int temp= 0;
temp = a[p];
a[p] = a[pos];
a[pos] = temp;
heapAdjust(a,p,maxLength);
}
}else{
cout<<"參數pos不合法!"<<endl;
return 0;
}
return a[0];
}
//獲取每一次排列時最小的數
/**
*@param int a[] 用來接收要排序的數組
*@param int maxLength 表示數組的長度
*@return int
*/
int GetHeap(int a[],int maxLength){
for(int i = maxLength-1;i>=0;i--){
heapAdjust(a,i,maxLength);
}
cout<<"---------------------------每一次找到的最小值:---------------------------"<<a[0]<<endl;
return a[0];
}
//用來排序的將每一次得到的最小值進行排序
/**
*@param int a[] 用來接收要排序的數組
*@param int maxLength 表示數組的長度
*@return int
*/
void heap_Sort(int a[],int maxLength){
int temp = 0;
int count = 0;
for(int i = maxLength-1;i>=0;i--){
GetHeap(a,i+1);//此處i+1是爲了保持數組的長度不變
temp = a[0];
a[0]=a[i];
a[i] =temp;
count++;
cout<<"count="<<count<<endl;
}
}
//寫一個輸出函數
/**
*@parame int a[] 用來接收需要輸出的數組長度
*@param int maxLength 表示數組的長度
*@return 無
*/
void out_put(int a[],int maxLength){
for(int i = 0;i < maxLength;i++){
cout <<"第"<<(i+1)<<"個元素:"<<a[i]<<endl;
}
}
int main(){
int a[11] = {11,9,1,4,2,3,5,7,6,8,0};
cout<<"未排序之前數組元素:"<<endl;
out_put(a,11);
heap_Sort(a,11);
cout<<"排序之後的數組元素:"<<endl;
for(int i = 10;i >=0;i--){
cout <<"第"<<(i+1)<<"個元素:"<<a[i]<<endl;
}
system("PAUSE");
return 0;
}
以上代碼經驗證過!