堆排序-學習筆記
阿新 • • 發佈:2017-07-29
scanf pre n-1 col 最小堆 維數 應用 調整 節點
在學習堆排序之前首先了解一下二叉堆的特性:
1、二叉堆的父節點的值總是大於等於(或小於等於)其左右孩子的值;
2、每個節點的左右子樹都是一棵這樣的二叉堆。
如果該二叉堆的父節點總是大於孩子節點,則叫做最大堆,如果父節點小於孩子節點,則叫做最小堆。
在堆排序的應用中,如果遞增排序,則應該使用最大堆,反之,使用最小堆。
堆排序是不穩定的
堆排序主要有兩個步驟來完成堆排序:
1、把一個無序數列構造成一個最大堆或最小堆;
2、去掉根節點(堆頂元素),把剩下的元素重新構造一個二叉堆。
二叉堆的存儲結構為一維數組,邏輯結構為二叉樹。
在一個索引以0為開頭的數組中,i為二叉堆的非葉子節點,它的左孩子為2*i+1,右孩子為2*i+2 ,他的父節點為(i-1)/2。
構造二叉堆:
1、選取最後一個非葉子節點,和他的左右孩子比較,如果有大於(小於)父節點的,則把左右孩子節點中較大的一個與父節點對換。
2、重復上述步驟,直到所有的節點全部掃描一遍,這樣,二叉堆就構造好了。
調整二叉堆:
1、把堆頂元素和最後一個元素互換,構成了一個新的數組 a[0...n-1];
2、把新構成二叉堆重新掃描,因為只是更換了堆頂元素,所以只要掃描堆頂元素就可以了,完成後,構成了一個新的二叉堆。
3、重復上述步驟,直到二叉堆內的元素為1;
4、最後,新構成的數組就是排好序的了。
代碼如下
#include<cstdio> void HeapadjustDown(inta[],int _begin,int _end){ //二叉堆構造函數 int temp=a[_begin]; //把節點保存在臨時temp裏 int lchild=2*_begin+1; //lchild為左孩子 while(lchild<_end){ //循環控制條件 if(lchild+1<_end&&a[lchild+1]>a[lchild]){ //從左右孩子中選取最大的 lchild++; } if(a[lchild]<temp){ //如果左右孩子節點都比父節點小,則退出break; } a[_begin]=a[lchild];//父節點被子節點替換 _begin=lchild;//把當前父節點設置為替換的孩子節點 lchild=2*_begin+1;//重新設置左孩子節點 } a[_begin]=temp;//把最開始的父節點賦值到,孩子節點中。 } void HeapSort(int a[],int n){ for(int i=(n-1)/2;i>=0;i--){//這個循環是構造二叉堆 HeapadjustDown(a,i,n); } for(int i=n-1;i>=0;i--){//這個循環是構造有序數組 int temp=a[i]; a[i]=a[0]; a[0]=temp; HeapadjustDown(a,0,i);//去掉堆頂元素後,重新構造二叉堆 } } int main(){ int n,a[20]; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } HeapSort(a,n); for(int i=0;i<n;i++){ printf("%d ",a[i]); } }
堆排序-學習筆記