排序算法之堆排序
1)基本思想
堆排序的基本思想基于优先队列,对于一个无序数组,首先构造出最小堆,然后每一次删除顶点处最小的元素,这样依次删除就可以得到一个降序排列的数组。在此过程中,如将删除的元素依次赋给一个数组,那么最终得到的是一个增序排列的数组,但是需要额外的数组空间。为了减少空间消耗,可以利用堆删除元素之后并重新构造之后的最后一个位置来存放刚刚删除的元素,最终得到的是一个降序数组。为了得到增序排列的数组,可以将最小堆改为最大堆,即顶点处的元素是最大的,称为maxHeap.
示例如下,首先删除顶点处元素97,并将最后一个元素移至顶点处,并重新构造最大堆:
2)算法实现
1 #include "stdafx.h" 2 #include <iostream> 3 using namespace std; 4 5 void print(int A[], int n) 6 { 7 for(int i = 0; i < n; i++) 8 { 9 cout<<A[i]<<" "; 10 } 11 cout<<endl; 12 } 13 14 void HeapDown(int A[], int i, int n) 15 { 16 int child; 17 int temp = A[i]; 18 for(; 2 * i + 1 < n; i = child) 19 { 20 child = 2 * i + 1; 21 if(child + 1 < n && A[child] < A[child+1]) 22 { 23 child++; 24 } 25 if(temp < A[child]) 26 { 27 A[i] = A[child]; 28 } 29 else 30 break; 31 } 32 A[i] = temp; 33 print(A,n); 34 } 35 36 void swap(int* a, int* b) 37 { 38 int temp = *a; 39 *a = *b; 40 *b = temp; 41 } 42 43 void heapSort(int A[], int n) 44 { 45 //construct the heap 46 int i; 47 for(i = n/2; i >= 0; i--) 48 { 49 HeapDown(A,i,n); 50 } 51 52 for(i= n-1; i > 0; i--) 53 { 54 swap(&A[0],&A[i]); 55 print(A,n); 56 HeapDown(A,0,i); 57 print(A,n); 58 } 59 60 print(A,n); 61 } 62 63 int _tmain(int argc, _TCHAR* argv[]) 64 { 65 int A[7] = {26, 41, 53, 58, 59, 31, 97}; 66 heapSort(A, 7); 67 system("pause"); 68 return 0; 69 }
3)复杂度分析
使用堆时,构建二叉堆的复杂度为O(N);每次执行DeleteMax或DeleteMin需要O(logN),N次删除的时间为O(NlogN);已排序的元素可以放到原来的堆中已删除元素的部分,也可以另外开辟一个数组来存放,区别是是否占用空间,复杂度均为N,因而堆排序的时间复杂度为O(NlogN).
堆排序对原始记录的状态并不敏感,因此它无论最好、最坏和平均时间复杂度均为O(NlogN)。由于记录的比较和交换是跳跃式进行,因此堆排序是一种不稳定的排序方法。