第二部分 排序和順序統計量 第 6 章 堆排序

排序算法
  插入排序最壞情況下可以再θ(n2) 時間內將 n 個數排好序。但是,由於其內層循環緊湊,對於小規模輸入,是一張非常快的原址排序(如果輸入數組中僅有常數個元素需要在排序過程中存儲在數組之外,則稱排序算法是原址的)。歸併排序不是原址的。
  第 6 章介紹的堆排序,是一種θ(nlgn) 時間的原址排序算法。
  第 7 章介紹快速排序,也是一種原址排序,但最壞情況時間爲θ(n2) ,然而期望運行時間爲θ(nlgn) ,而且在實際應用中,通常比堆排序快。與插入排序類似,快速排序的代碼也很緊湊,因此運行時間中隱含的常數係數很小。快速排序是排序大數組的最常用算法。
這裏寫圖片描述
  順序統計量
  一個n個數的集合的第 i 個順序統計量就是集合中第 i 個小的數。當然,我們可以通過將輸入集合排序,取輸出的第 i 個元素來選擇第 i 個順序統計量。當不知道輸入數據的分佈時,這種方法的運行時間爲Ω(nlgn),即第 8 章所證明的比較排序算法的下界。
  在第 9 章,我們展示了即使輸入數據是任意實數,也可以在O(n)時間內找到第 i 個小的元素。我們提出了一種隨機算法,其僞代碼非常緊湊,它的最歡情況運行時間爲θ(n2) ,但期望運行時間爲O(n)。
  

第 6 章 堆排序

  堆排序的時間複雜度是θ(nlgn) ,但不同於歸併排序的是,堆排序同樣具有空間原址性:任何時候都只需要常數個額外的元素空間存儲臨時數據。因此,堆排序是集合了歸併排序和插入排序兩種排序算法的優點。
  堆排序引入了另一種算法設計技巧:使用一種我們稱爲“堆”的數據結構來進行信息管理。堆不僅用在堆排序中,而且它也可以構造一種有效的優先隊列。

6.1 堆

  (二叉)堆是一個數組,它可以被看成一個近似的完全二叉樹。樹上的每一個節點對應數組中的一個元素。除了最底層外,該數是完全充滿的,而且是從左向右填充。表示堆的數組A包括兩個屬性:A.length(通常)給出數組元素的個數,A.heap-size表示有多少個堆元素存儲在該數組中。這裏:0 <= A.heap-size <= A.length。
  這裏寫圖片描述
  圖6-1 以二叉樹和數組形式展現的一個最大堆。每個節點圓圈內部的數字是它所存儲的數據。節點上方的數字是它在數組中相應的下標。數組上方和下方的連線顯示的是父子關係:父節點總是在它的孩子節點的左邊。該數的高度爲 3,下標爲4(值爲8)的節點的高度爲1.
  
數的根節點是A[1],給定一個節點的下標i,可以很容易得到它的父節點、左孩子和右孩子的小標:

PARENT(i)
    return i/2
LEFT(i)
    return 2i
RIGHT(i)
    return 2i+1

  二叉樹可以分爲兩種形式:最大堆和最小堆。在這兩個堆中,節點的值都要滿足堆的性質,但一些細節定義有所差異。在最大堆中,最大堆性質是指除了滿足根以外的所有節點 i 都要滿足:
  A[PARENT(i)]>=A[i]
也就是說,某個節點的值至多與其父節點一樣大。因此,堆中的最大元素存放在根節點中。最小堆的組織方式正好相反:最小堆性質是指除了根以外的所有節點 i 都有
  A[PARENT(i)]>=A[i]
最小堆的最小元素存放在根節點中。
  在堆排序算法中,我們使用的是最大堆。最小堆通常用於構造優先隊列。
  我們定義一個堆中的節點的高度就爲該節點到葉節點最長簡單路徑上邊的數目;進而,我們可以把堆的高度定義爲根節點的高度。既然一個包含 n 個元素的隊可以看做一顆完全二叉樹,那麼該堆的高度是θ(lg n)。我們會發現,堆結構上的一些基本操作的運行時間至多與數的高度成正比,即時間複雜度爲O(lgn)。在本章剩餘部分將介紹一些基本過程:
  

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

6.2 維護堆的性質

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

l = LEFT(i)
r = RIGHT(i)
if l <= A.heap-size and A[l] > A[i]
    largest = l
else largest = i
if r <= A.heap-size and A[r] > A[largest]
    largest = r
if largest != i
    exchangeA[i] with A[largest]
    MAX-HEAPY(A, largest)

我們用下面的遞歸式刻畫MAX-HEAPIFY的運行時間:
T(n)<= T(2n/3) + θ(1)
根據主定理,上述的解爲T(n)= O(lgn)。也就是說,對於一個樹高爲h的節點來說,MAX-HEAPIFY的時間複雜度爲O(h)。

6.3 建堆

  我們可以用自底向上的方法利用MAX-HEAPIFY把一個大小爲n=A.length的數組A[1,…,n]轉換爲最大堆。過程BUILD-MAX-HEAP對數中的其他節點都調用一次MAX-HEAPIFY。
BUILD-MAX-HEAP(A)

A.heap-size = A.length
for i = A.length/2 downto 1
    MAX-HEAPIFY(A,i)

BUILD-MAX-HEAP的時間複雜度爲O(n)。因此,我們可以在線性時間內,把一個無序數組構造稱爲一個最大堆。
  類似地,我們也可以通過調用一個BUILD-MIN-HEAP構造一個最小堆。只需修改第 3 行的調用替換爲MIN-HEAPIFY。

6.4 堆排序算法

  初始時候,堆排序算法利用BUILD-MAX-HEAP將輸入數組A[1..n]建成最大堆,其中n = A.length。因爲數組中的最大元素總在根節點A[1]中,通過把它與A[n]進行交換,可以讓該元素放到正確的位置。這是,去掉節點n,剩餘的節點中,原來的根的節點仍然是最大堆,而新的跟節點可能會違背最大堆的性質。爲了維護最大堆的性質,調用MAX-HEAPIFY(A, 1),從而在A[1..n-1]上構造一個新的最大堆。堆排序算法會不斷重複這一過程,知道堆的大小從n-1降到2.
HEAPSORT(A)

BUILD-MAX-HEAP(A)
for i = A.length downto 2
    exchange A[1] with A[i]
    A.heap-size = A.heap-size - 1
    MAX-HEAPIFY(A, 1)

HEAPSORT過程的時間複雜度是O(nlgn)。

6.5 優先隊列

  和堆一樣,優先隊列也有兩種形式:最大優先隊列和最小優先隊列。
  優先隊列是一種用來維護由一組元素構成的結合S的數據結構,其中的每一個元素都有一個相關的值,稱爲關鍵字。一個最大優先隊列支持以下操作:
  INSERT(S, x):把元素x插入集合S中。這一操作等價於S=SU{x}
  MAXIMUM(S):返回S中具有最大鍵字的元素。
  EXTRACT-MAX(S):去掉並返回S中的具有最大鍵字的的元素。
  INCREASE-KEY(S,x,k):將元素x的關鍵字值增加到k,這裏假設k的值不小於x的原關鍵字值。
  最大優先隊列的應用有很多,其中一個就是在共享計算機系統的作業調度。
  現在我們來討論如何實現最大優先隊列的操作。過程HEAP-MAXIMUM可以在θ(1)時間內實現MAXIMUM操作。
HEAP-MAXIMUM(A)  

return A[1]

過程HEAP-EXTRACT-MAX實現EXTRACT-MAX操作。
HEAP-EXTRACT-MAX(A)

if A.heap-size < 1
    errror "heap underflow"
max = A[1]
A[1] = A[A.heap-size]
A.heap-size = A.heap-size - 1
MAX-HEAPIFY(A, 1)
return max

HEPA-INCREASE-KEY能夠實現INCREASE-KEY操作。
HEPA-INCRESE-KEY(A,i,key)

if key < A[i]
    error"new key is smaller than current key"
A[i] = key;
while i > 1 and A[PARENT(i)] < A[i]
    exchange A[i] with A[PARENT(i)]
    i = PARENT(i)

MAX-HEAP-INSERT能夠實現INSERT操作。
MAX-HEAP-INSERT(A,key)

A.heap-size = A.heap-size + 1
A[A.heap-size] = -
HEAP-INCREASE-KEY(A, A.heap-size, key)

在包含n個元素的堆上,MAX-HEAP-INSERT的運行時爲O(lgn)。
總之,在一個包含n個元素的堆中,所有優先隊列的操作都可以在O(lgn)時間內完成。
練習:
1. 當用數組表示存儲n個元素的堆時,葉節點的下標分別爲⌊n/2⌋+1,⌊n/2⌋+2, … ,⌊n/2⌋+n.
2. 對於任一包含n個元素的堆中,至多有n/2h+1 個高度爲h的節點。
3.

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