堆的實現、堆排序及其應用


堆排序和其他排序算法的比較

 

上圖引自: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);
    }
}

 

如有任何錯誤或者不足,歡迎不吝指出。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM