一、堆的概念
所謂堆,它是一個數組,也能夠被看成一個近似的全然二叉樹。樹上每一個結點相應數組的一個元素。二叉堆分為二種:最大堆和最小堆。本文主要介紹最大堆,最小堆類似。最大堆的特點:對於隨意某個結點,該結點的值大於左孩子、右孩子的值,可是左右孩子的值沒有要求。
二、堆排序算法
首先,按堆的定義將數組R[0..n]調整為堆(這個過程稱為創建初始堆),交換R[0]和R[n];
然后,將R[0..n-1]調整為堆,交換R[0]和R[n-1];
如此反復,直到交換了R[0]和R[1]為止。
以上思想可歸納為兩個操作:
(1)根據初始數組去構造初始堆(構建一個完全二叉樹,保證所有的父結點都比它的孩子結點數值大)。
這里可以利用完全二叉樹的結構,從最后一個非終端節點開始對子元素進行排序篩選。
(2)每次交換第一個和最后一個元素,輸出最后一個元素(最大值),然后把剩下元素重新調整為大根堆。
當輸出完最后一個元素后,這個數組已經是按照從小到大的順序排列了。
具體圖片我就不找了,網上很多。
三、算法比較
堆排序算法的時間復雜度是O(nlgn),比插入排序要好,跟歸並排序相同,但是與歸並排序不一樣的地方在於,堆排序不需要額外的存儲空間,或者說,只需要常數個額外的存儲空間,屬於內排序算法。
#include <stdio.h> #define N 1000 #define INF 999999999 int h[N]; //調整堆(迭代法) //n:規模 i:二叉子堆的堆頂 void heapAdjust(int n, int par) { int tmp, pos, lc, rc; while (par <= n/2) { tmp = h[par]; //記錄父母結點鍵值 lc = par<<1; rc = lc+1; pos = par; //父母結點至多更新2次 if (h[par] < h[lc]) { h[par] = h[lc]; pos = lc; } if (rc <= n && h[par] < h[rc]) { h[par] = h[rc]; pos = rc; } if (pos == par) //無更新即無需調整 break; else h[pos] = tmp; par = pos; //假設這個位置的結點是“父母結點” } } //創建堆 //規模為n的堆,對其父母結點,自底向上自右向左地調整堆 void createHeap(int n) { int i; for (i = n/2; i != 0; i--) { heapAdjust(n, i); } } void heapSort(int n) { int ntimes = n; while (ntimes--) { printf("%d\n", h[1]); h[1] = h[n]; h[n--] = 0; //堆清零 heapAdjust(n, 1); } } int main(void) { int n, i; scanf("%d", &n); h[0] = INF; for (i = 1; i != n+1; i++) { scanf("%d", &h[i]); } createHeap(n); heapSort(n); return 0; } /* 參考測試數據 6 342 31 52 626 12 124 10 43 525 14 21 52 3 52 45 319 15155 */
