堆排序算法——Java實現

堆排序

1、堆

    如圖6-1所示,(二叉)堆是一個數組,它可以被看成一個近似的完全二叉樹。樹上的每一個結點對應數組中的一個元素。除了最底層外,該樹是完全充滿的,而且是從左向右填充。表示堆的數組A包括兩個屬性:A.length(通常)給出數組元素的個數,A.heap-size表示有多少個堆元素存儲在該數組中。也就是說,雖然A[1..A.length]可能都存有數據,但只有A[1..A.heap-size]中存放的是堆的有效元素,這裏,0<=A.heap-size<=A.length。樹的根節點是A[1],這樣給定一個結點的下標i,我們很容易計算得到它的父結點、左孩子和右孩子的下標:

PARENT(i)
<span style="white-space:pre">	</span>return『i/2』

LEFT(i)
<span style="white-space:pre">	</span>return 2i
RIGHT(i)
<span style="white-space:pre">	</span>return 2i+1

    在大多數計算機上,通過將i的值左移一位,LEFT過程可以在一條指令內計算出2i。採用類似方法,在RIGHT過程中可以通過將i的值左移1位並在低位加1,快速計算得到2i+1.至於PARENT過程,則可以通過把i的值右移1位計算得到『i/2』。在堆排序的實現中,這三個函數通常是以“宏”或者“內聯函數”的方式實現的。

二叉堆可以分爲兩種形式:最大堆和最小堆。在這兩種堆中,結點的值都要滿足堆的性質,但一些細節定義則有所差異。在最大堆中,最大堆性質是指除了根以外的所有結點i都要滿足:

A[PARENT(i)]>=A[i]

也就是說,某個結點的值至多與其父結點一樣大。因此,堆中的最大元素存放在根節點中;並且,在任一子樹中,該子樹所包含的所有結點的值都不大於該子樹根結點的值。

下面我們開始介紹一些基本過程,並說明如何在排序算法中應用它們。

  • MAX-HEAPIFY過程:其時間複雜度爲O(lgn),它是維護最大堆性質的關鍵。
  • BUILD-MAX-HEAP過程:具有線性時間複雜度,功能是從無序的輸入數據數組中構造一個最大堆。
  • HEAPSORT過程:其時間複雜度爲O(lgn),功能是對一個數組進行原址排序。
  • MAX-HEAP-INSERT、HEAP-EXTRACT-MAX、HEAP-INCREASE-KEY和HEAP-MAXIMUM過程:時間複雜度爲O(lgn),功能是利用對實現一個優先隊列。

2、維護堆的性質

MAX-HEAPIFY是用於維護最大堆性質的重要過程。它的輸入爲一個數組A和一個下標i。在調用MAX-HEAPIFY的時候,我們假定根結點爲LEFT(i)和RIGHT(i)的二叉樹都是最大堆,但這時A[i]有可能小於其孩子,這樣就違背了最大堆的性質。MAX-HEAPIFY通過讓A[i]的值在最大堆中“逐級下降”,從而使得以下標i爲根結點的子樹重新遵循最大堆的性質。

MAX-HEAPIFY(A,i)
<span style="white-space:pre">	</span>l=LEFT(i)
<span style="white-space:pre">	</span>r=RIGHT(i)
<span style="white-space:pre">	</span>if l<=A.heap-size and A[l]>A[i]
<span style="white-space:pre">		</span>largest=l
<span style="white-space:pre">	</span>else largset=l
<span style="white-space:pre">	</span>if r<=A.heap-size and A[r]>A[largest]
<span style="white-space:pre">		</span>largest=r;
<span style="white-space:pre">	</span>if largest!=i
<span style="white-space:pre">		</span>exchange A[i] with A[largest]
<span style="white-space:pre">		</span>MAX-HEAPIFY(A,largest)

    下圖6-2圖示了MAX-HEAPIFY的執行過程。在程序的每一步中,從A[i]、A[LEFT(i)]和A[RIGHT(i)]中選出最大的,並將其下標存儲在largest中。如果A[i]是最大的,那麼以i爲根結點的子樹已經是最大堆,程序結束。否則,最大元素是i的某個孩子結點,則交換A[i]和A[largest]的值。從而使i及其孩子都滿足最大堆的性質。在交換後,下標爲largest的結點的值是原來的A[i],於是以該結點爲根的子樹又有可能會違反最大堆的性質。因此,需要對該子樹遞歸調用MAX-HEAPIFY。


3、建堆

    我們可以用自底向上的方法利用過程MAX-HEAPIFY把一個大小爲n=A.length的數組A[1..n]轉換爲最大堆。我們知道,子數組A(『n/2』+1..n)中的元素都是樹的葉結點。每個葉結點都可以看成只包含一個元素的堆。過程BUILD-MAX-HEAP對樹中的其他結點都調用一次MAX-HEAPIFY。

BUILD-MAX-HEAP(A)
<span style="white-space:pre">	</span>A.heap-size=A.length
<span style="white-space:pre">	</span>for i=[A.length/2] downto 1
<span style="white-space:pre">		</span>MAX-HEAPIFY(A,i) 
   圖6-3給出了BUILD-MAX-HEAP過程的一個例子。

4、堆排序算法

    初始時候,堆排序算法利用BUILD-MAX-HEAP將輸入數組A[1..n]建成最大堆,其中n=A.length。因爲數組中的最大元素總在根結點A[1]中,通過把它與A[n]進行互換,我們可以讓該元素放到正確的位置。這時候,如果我們從堆中去掉結點n(這一操作可以通過減少A.heap-size的值來實現),剩餘的結點中,原來根的孩子結點仍然是最大堆,而新的根結點可能會違背最大堆的性質。爲了維護最大堆的性質,我們要做的是調用MAX-HEAPIFY(A,1),從而在A[1..n-1]上構造一個新的最大堆。堆排序算法會不斷重複這一過程,直到堆的大小從n-1降到2。

HEAPSORT(A)
<span style="white-space:pre">	</span>BUILD-MAX-HEAP(A)
<span style="white-space:pre">	</span>for i=A.length downto 2
<span style="white-space:pre">		</span>exchange A[i] with A[i]
<span style="white-space:pre">		</span>A.heap-size=A.heap-size - 1
<span style="white-space:pre">		</span>MAX-HEAPIFY(A,1)
     圖6-4給出了一個在HEAPSORT的第1行建立初始最大堆之後,堆排序操作的一個例子。圖6-4顯示了第2~5行for循環第一次迭代開始前最大堆的情況和每一次迭代之後最大堆的情況。

5、Java代碼實現

後續補充。。。。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章