簡單就能弄懂堆排序

對大多數學習數據結構的同學來說,堆是感覺上很難懂的一個數據結構,更何況用堆進行的操作,但你又不得不承認,堆在算法中絕對算一個很高效很重要的數據結構。大家弄不明白的原因大概是沒有靜下來去深究堆,筆者曾經也一樣,但最近靜下心來着實認真的梳理了一下堆,覺得只要弄懂以下幾點,其實,堆也就是那麼回事。

一、理解堆的定義

滿足兩個條件:

(1)滿足完全二叉樹的性質(要理解完全二叉樹的性質,如果不懂的話翻翻塵封的書你就知道了)

(2)對於每個父節點Ki,滿足Ki<=K2i+1且Ki<=K2i+2(或者Ki>=K2i+1且Ki>=K2i+2),分別稱爲最小堆和最大堆。

二、堆的構造思想(篩選法)

    (1)初始化:將初始的關鍵字按層次的次序放到用一維數組表示的一棵完全二叉樹的各個結點中。
    (2)從子樹建堆:很明顯,因爲是完全二叉樹,所有i>(n-2)/2後面的結點ki都是葉結點(葉節點符合堆的性質,已經是堆不必調整),接下來要做的就是需要逐步把從 i = (n-2)/2開始結點ki,ki-1,...k0爲根的子樹逐次的排成堆。最後形成建堆的過程。(n指的是堆元素的總數)

三、建堆的算法與實現

下面我們以最小堆爲例子來說明建堆過程。首先就要從序列索引 i = (n-2)/2的爲根的結點Ki爲子樹開始形成最小堆。

(1)循環,步長爲1,從 i = (n-2)/2開始到i= 0(整個樹的根節點)位置,進行如下操作:逐次比較ki、k2i+1(Ki的左孩子)、k2i+2(ki的右孩子,如果存在的話)的大小,找到其中最小的數值賦給ki,因爲這次過程可能會破壞之前更小子樹建好的最小堆,此時我們還需要以同樣的方式向下進行篩選,直到這整個子樹形成最小堆爲止。這就是在建堆中必不可少的向下的篩選法:FilterDown(int heap,int i,int n))

//篩選法調整堆 
void FilterDown(int *heap,int start,int n)
{
     int i = start,j = 2*i+1;//i node and the left child of i
     int temp = heap[i];
     while(j<n)
     {
            if(j+1<n && heap[j]>heap[j+1])
                     j++;//找到heap[i],heap[2i+1],heap[2i+2]中的最小值位置,然後賦值給heap[i] 
                     
            if(heap[j]>temp) break;//heap[i]已經是最小的了 
            else
            {
                heap[i] = heap[j];//把三者中最小的值賦給子樹父節點 i
                i = j;//對子樹的子樹進行相應的最小堆構造 
                j = 2*i+1;// 置換參數 
            }
            heap[i] = temp;     
     }
}

(2)循環對ki,ki-1,...k0的爲根結點的子樹進行向下篩選,直到整個完全二叉樹的根爲止。
//創建最小堆 
void CreateMinHeap(int *heap,int n)
{
     int start = (n-2)/2;
     for(start = (n-2)/2;start>=0;start--)
     {
         FilterDown(heap,start,n); 
     }
}

四、堆排序應該怎麼做?

很顯然,形成最小堆之後最小的元素就是整個完全二叉樹的根,每次挑出形成最小堆的根元素(根節點,即heap[0]),挑出來後進行交換然後在進行除這個元素以外更小規模的最小堆創建,依次找到各個規模更小的堆中的根元素,直到最後一個元素爲止,這時候就能對這些元素進行排序了。

//最小堆排序 
void MinHeapSort(int *heap,int n)
{
     int size;
     CreateMinHeap(heap,n);//建成最小堆可以得到最小的一個元素 
     for(size = n-1;size>0;size--)
     {
             swap(heap[0],heap[size]);//交換堆頂和最後一個元素,即將最小元素放到最後面
             FilterDown(heap,0,size);//重新調整堆頂節點成爲小頂堆           
     }      
}

示例展示:



五、總結

堆排序是適合排序文件中的元素個數較大的情況。對於n個元素進行堆排序的時,其時間複雜度是O(nlog2n)。附加的存儲空間需要一個只要求用於交換的節點,因此空間複雜度爲O(1)。你弄明白了嗎?











發佈了33 篇原創文章 · 獲贊 18 · 訪問量 79萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章