排序---堆排序算法
做爲選擇排序的改進版,堆排序能夠把每一趟元素的比較結果保存下來,以便咱們在選擇最小/大元素時對已經比較過的元素作出相應的調整。數組
做爲選擇排序的改進版,堆排序能夠把每一趟元素的比較結果保存下來,以便咱們在選擇最小/大元素時對已經比較過的元素作出相應的調整。性能
堆排序是一種樹形選擇排序,在排序過程當中能夠把元素當作是一顆徹底二叉樹,每一個節點都大(小)於它的兩個子節點,當每一個節點都大於等於它的兩個子節點時,就稱爲大頂堆,也叫堆有序; 當每一個節點都小於等於它的兩個子節點時,就稱爲小頂堆。3d
下面是咱們要保存在數組中的堆的形式code
1.將長度爲n的待排序的數組進行堆有序化構形成一個大頂堆 2.將根節點與尾節點交換並輸出此時的尾節點 3.將剩餘的n -1個節點從新進行堆有序化 4.重複步驟2,步驟3直至構形成一個有序序列
{5, 2, 6, 0, 3, 9, 1, 7, 4, 8}
在構造有序堆時,咱們開始只須要掃描一半的元素(n/2-1 ~ 0)便可,爲何?blog
由於(n/2-1)~0的節點纔有子節點,如圖1,n=8,(n/2-1) = 3 即3 2 1 0這個四個節點纔有子節點排序
第一次找到[n/2]處,進行構造:for循環
咱們比較父節點,左右孩子結點的大小,將最大的做爲堆頂性能分析
第二次,咱們對上次找到位置-1便可,找到結點0,對其左右孩子比較,構造這三個結點的最大堆二叉樹
第三次,咱們找到結點6,要對其進行構造,結果以下
第四次(重點),咱們不止要構造雙親和左右孩子,咱們還要比較其孩子結點爲根的堆是否正確,否則咱們須要進行調整
咱們發現將8,7,2三個結點變爲了最大堆,可是其中2,3子樹再也不是一個最大堆,咱們須要對其修改
第五次:選取結點9進行構造
發現以結點5爲根的子樹不是最大堆,咱們須要進行調整
void swap(int K[], int i, int j) { int temp = K[i]; K[i] = K[j]; K[j] = temp; } //大頂堆的構造,傳入的i是父節點 void HeapAdjust(int k[],int p,int n) { int i,temp; temp = k[p]; for (i = 2 * p; i <= n;i*=2) //逐漸去找左右孩子結點 { //找到兩個孩子結點中最大的 if (i < n&&k[i] < k[i + 1]) i++; //父節點和孩子最大的進行判斷,調整,變爲最大堆 if (temp >= k[i]) break; //將父節點數據變爲最大的,將原來的數據仍是放在temp中, k[p] = k[i]; //如果孩子結點的數據更大,咱們會將數據上移,爲他插入的點提供位置 p = i; } //當咱們在for循環中找到了p子樹中,知足條件的點,咱們就加入數據到該點p,注意:p點原來數據已經被上移動了 //若沒有找到,就是至關於對其值不變 //插入 k[p] = temp; } //大頂堆排序 void HeapSort(int k[], int n) { int i; //首先將無序數列轉換爲大頂堆 for (i = n / 2; i > 0;i--) //注意因爲是徹底二叉樹,因此咱們從一半向前構造,傳入父節點 HeapAdjust(k, i, n); //上面大頂堆已經構造完成,咱們如今須要排序,每次將最大的元素放入最後 //而後將剩餘元素從新構造大頂堆,將最大元素放在剩餘最後 for (i = n; i >1;i--) { swap(k, 1, i); HeapAdjust(k, 1, i - 1); } } int main() { int i; int a[11] = {-1, 5, 2, 6, 0, 3, 9, 1, 7, 4, 8 }; HeapSort(a, 10); for (i = 1; i <= 10; i++) printf("%d ", a[i]); system("pause"); return 0; }
運行時間主要消耗在構造堆和重建堆時的反覆篩選上。 構造堆的時間複雜度爲O(n) 重建堆時時間複雜度爲O(nlogn)。 因此整體就是O(nlogn)。 不適合排序序列個數較少的狀況