堆排序和其他排序算法的比較
上圖引自:http://hi.baidu.com/ycdoit/item/6b5f5b9571a843becc80e560,這篇文章也解釋了可能的優化途徑。
(sort 是STL的排序方法,qsort是庫函數,quicksort是沒有經過優化的快速排序實現,Heapsort是一般的堆排序實現)
堆的定義
談堆排序首先要有堆,堆類似於一種特殊的完全二叉樹,分為最大堆和最小堆。
最大堆:所有結點的值都不大於其父結點。
最小堆:所有結點的值都不小於其父結點。
關於堆的定義和特性,請參考http://blog.csdn.net/v_JULY_v/article/details/6198644,非常好的一篇關於堆排序的文章。
堆的實現
談這個話題是寫這篇文章的主要原因。
我們常說程序就是數據結構和算法的組合,具體到堆的實現上,算法體現出了非常強大的抽象作用,表現在:
1. 存儲最小堆或者最大堆的數據結構就是一個簡單的數組。
2. 算法實現的時候,把這個數組看作一種完全二叉樹:
(1) 從根結點開始(編號為1),分層遍歷這顆二叉樹,每個結點的編號是上一個遍歷到的結點編號+1,就是說:
1
/ \
2 3
/ \
4 5
(2) 二叉樹各個結點按照編號順序存在數組中。
(3) 對於完全二叉樹來說,下面的條件成立:
- 第i個結點的父結點為第|_i/2_|個結點(|_i/2_|表示對i/2的結果向下取整)。
- 第i個結點的左孩子結點為第2i個結點。
- 第i個結點的右孩子結點為第2i+1個結點。
帶着上面的邏輯來實現最小堆和最大堆,就是把父結點和左右子結點的大小關系映射為數組中第i個數據和第2i以及第2i+1個數據的大小關系。
這里我們沒有定義二叉樹的數據結構,而是在處理數組的時候將其看作分層遍歷的完全二叉樹結點,具體表現在算法的實現上面。
很多人在想到堆的時候就想到二叉樹的數據結構,進而認為堆的實現就基於這種數據結構,堆排序也是對二叉樹的排序,這是不准確的,其實我們的操作,包括創建一個最小堆或最大堆,以及進行堆排序都是在上面映射關系的基礎上,用處理數組元素的方式進行的。
當然,可以用二叉樹的數據結構,但是考慮到這種數據結構相比數組而言,每個結點的大小增加了,而堆排序的算法卻沒有優化,還是平均N*log2(N)(N為結點數量),必要性在哪里?
所以,有時候一種不同的對待事物的方式可以極大提高我們的編程效率,這就是算法的威力所在。
尋找M個數中的前K個最小的數並保持有序
這個算法題在這篇文章中得到了精彩詳盡的討論,強烈推薦感興趣的同學學習一下。
這里我們使用最大堆來解決:
1. 用M個數里面的前K個數創建一個K個元素的最大堆。
2. 將第M-k個數到第M個數依次和上面最大堆的第一個元素(存儲這個最大堆的第1個數據,也即是抽象的完全二叉樹的根結點數據)比較,如果大於等於堆頂元素,繼續下一個數進行比較(堆頂元素在最大堆中最大),如果小於堆頂元素,就把堆頂元素值設置為這個數,然后重新對這個堆進行重新最大堆化,使之符合最大堆的要求。
3. 結束之后最大堆里面放的就是最小的K個數了,這時對最大堆進行堆排序,就解決這個問題了。
時間復雜度為 :
O(K) + (M - K)*log2(K)
| |
創建K個元素最大堆的時間復雜度 對剩余M-K個數據進行比較並每次對最大堆進行重新最大堆化
在K遠小於M的時候,時間復雜度相當於M*log2(K)。
完整代碼實現:
P.S.如果需要在M非常大的情況下測試,可以考慮將M個數據保存在文件中,然后依次讀取進行處理。
#include <iostream> using namespace std; /****************** 根據輸入的int數組創建一個最大堆 input: 輸入的int數組 maxHeap: 最大堆數組 maxHeapCount 最大堆中元素的個數 ******************/ void createMaxHeap(const int * input, int * maxHeap, const int maxHeapCount); /****************** 對輸入int數組進行操作,使之符合最大堆的條件: 所有結點的值不大於其父結點 maxHeap: 最大堆數組 pos: 最大堆中的元素標志位,由1開始 maxHeapCount 最大堆中元素的個數 ******************/ void heapifyMaxHeap(int * maxHeap, const int pos, const int maxHeapCount); /****************** 對最大堆排序,使之由小到大有序 maxHeap: 最大堆數組 maxHeapCount 最大堆中元素的個數 ******************/ void maxHeapSort(int * maxHeap, const int maxHeapCount); /****************** 根據指定的長度初始化輸入的int數組 bigData: 輸入的int數組 arrayLength: 數組長度 ******************/ void initBigDataArray(int * bigData, const int arrayLength); void main() { const int M = 1000; const int K = 5; int bigDataM[M]; int maxHeap[K]; initBigDataArray(bigDataM, M); createMaxHeap(bigDataM, maxHeap, K); for(int step = K; step < M; ++step) { if(bigDataM[step] < maxHeap[0]) { maxHeap[0] = bigDataM[step]; heapifyMaxHeap(maxHeap, 1, K); } } maxHeapSort(maxHeap, K); cout<<"bigData array is: "; for(int step = 0; step < M; ++step) { cout<<bigDataM[step]<<" "; } cout<<endl; cout<<"Output maxHeap from less to larger: "; for(int step = 0; step < K; ++step) { cout<<maxHeap[step]<<" "; } cout<<endl; cout<<"Output maxHeap from larger to less: "; for(int step = 0; step < K; ++step) { cout<<maxHeap[K - 1 - step]<<" "; } cout<<endl; return; } void heapifyMaxHeap(int * maxHeap, const int pos, const int maxHeapCount) { int left = 2 * pos; int right = 2 * pos + 1; int largestElemPos = 0; if(left <= maxHeapCount && maxHeap[left - 1] > maxHeap[pos - 1]) largestElemPos = left; else largestElemPos = pos; if(right <= maxHeapCount && maxHeap[right - 1] > maxHeap[largestElemPos - 1]) largestElemPos = right; if(largestElemPos != pos) { swap(maxHeap[pos - 1], maxHeap[largestElemPos - 1]); heapifyMaxHeap(maxHeap, largestElemPos, maxHeapCount); } } void createMaxHeap(const int * input, int * maxHeap, const int maxHeapCount) { for(int step = 0; step < maxHeapCount; ++step) { maxHeap[step] = input[step]; } for(int startHeapifyPos = maxHeapCount/2; startHeapifyPos >= 1; --startHeapifyPos) { heapifyMaxHeap(maxHeap, startHeapifyPos, maxHeapCount); } } void initBigDataArray(int * bigData, const int arrayLength) { for(int i = 0; i< arrayLength; ++i) { bigData[i] = rand(); } } void maxHeapSort(int * maxHeap, const int maxHeapCount) { int maxHeapSize = maxHeapCount; for(int step = maxHeapSize; step >=2; --step) { swap(maxHeap[step - 1], maxHeap[0]); maxHeapSize -=1; heapifyMaxHeap(maxHeap, 1 , maxHeapSize); } }
如有任何錯誤或者不足,歡迎不吝指出。