堆排序,與歸併排序同樣,時間複雜度爲O(nlgn),與插入排序同樣,具備空間原址性:任什麼時候候都只須要常數個額外的元素空間存儲臨時數據。(二叉)堆是一個數組,能夠被當作一個近似的徹底二叉樹,樹上的每個結點對應數組中的一個元素。除了最底層外,該樹是徹底充滿的,並且是從左向右填充。表示堆的數組A有兩個屬性:A.length爲數組元素的個數,A.heap-size表示有多少個堆元素存儲在數組中。樹的根結點爲A[0],容易獲得父結點、左孩子和右孩子的下標:
二叉堆分爲最大堆和最小堆。結點的值都要知足堆序性質,最大堆中,堆中的最大元素存放在根結點中;而且,在任一子樹中,該子樹所包含的全部結點的值都不大於該子樹根結點的值,最小堆性質相反。
堆的基本過程:
Max-Heapify過程:時間複雜度爲O(lgn),是維護最大堆性質的關鍵。
Build-Max-Heap過程:具備線性時間複雜度,功能是從無序的輸入數據數組中構造一個最大堆。
HeapSort過程:時間複雜度爲O(nlgn),功能是對一個數據進行原址排序。
Max-Heap-Insert、Heap-Extract-Max、Heap-Increase-Key和Heap-Maximum過程:時間複雜度爲O(lgn),功能是利用堆實現一個優先隊列。
Max-Heapify的輸入爲一個數組A和一個下標i,經過讓A[i]的值在最大堆中「逐級降低",從而使得如下標i爲根結點的子樹從新遵循最大堆的性質,可經過遞歸和非遞歸兩種方式實現,代碼以下:
1 void 2 percDownRecursion(int a[],int i, int n) // max-heapify 下濾 維護最大堆性質 遞歸方式 3 { 4 int left = left(i); 5 int right = right(i); 6 int large; 7 if(left < n && a[left] > a[i]) 8 large = left; 9 else 10 large = i; 11 if(right < n && a[right] > a[large]) 12 large = right; 13 if(large != i){ 14 swap(a[i],a[large]); 15 percDownRecursion(a,large,n); 16 } 17 }
1 void percDown(int a[],int i, int n) //max-heapify 下濾 維護最大堆性質 非遞歸方式 2 { 3 int child; 4 int tmp; 5 6 for(tmp = a[i]; left(i) < n; i = child) 7 { 8 child = left(i); 9 if(child != n-1 && a[child + 1] > a[child]) 10 child++; 11 if(tmp < a[child]) 12 a[i] = a[child]; 13 else 14 break; 15 } 16 a[i] = tmp; 17 }
建堆,能夠用自底向上的方法利用過程Max-Heapify把一個大小爲n的數組A轉換爲最大堆,A[n/2]後的元素都是樹的葉結點,每一個葉結點均可以當作只包含一個元素的堆,時間複雜度爲O(n)。。算法
1 void buildMaxHeap(int a[], int n) 2 { 3 for(int i = n/2; i >= 0; i--) // build heap 構建最大堆 4 percDownRecursion(a,i,n); 5 }
堆排序,堆排序算法利用Build-Max-Heap將輸入數組A建成最大堆,由於數組中的最大元素總在根結點A[0]中,經過把它與A[n-1]進行互換,可讓該元素放到正確的位置,縮減堆的大小並進行下濾,從而在A[0...n-2]上構造一個新的最大堆。堆排序算法會不斷重複這一過程,直到堆的大小從n-1降到1。api
1 void 2 heapSort(int a[], int n) 3 { 4 buildMaxHeap(a,n); 5 for(int i = n-1; i > 0; i--) 6 { 7 swap(a[0],a[i]); 8 percDownRecursion(a,0,i); //或者調用percDown(a,0,i) 9 } 10 }
例子:數組
1 int main() 2 { 3 int a[] = {97,53,59,26,41,58,31}; 4 int size = 7; 5 heapSort(a,size); 6 for(int i=0; i < size; ++i) 7 printf("%d ",a[i]); 8 printf("\n"); 9 }
輸出:ui