資料結構與演算法分析 07 堆排序

2021-08-20 08:54:01 字數 3183 閱讀 7499

3.建堆

4.堆排序演算法

(二叉)堆資料結構是一種陣列物件,可以被視為一顆完全二叉樹。表示堆的陣列a是乙個具有兩個屬性的物件:length[a]是陣列中元素個數,heap-size[a]是存放在a中的堆的元素個數,heap-size[a]<=length[a]。樹的根表示為a[1],給定某結點下標i,其父節點parent(i) = i/2(向下取整),left[i] = 2i,right[i] = 2i+1。

max-heapify是對最大堆操作的重要子程式,其輸入為乙個陣列a和下標i,當a[i]小於其子女時可能違背最大堆性質,為了保持堆的性質,呼叫max-heapify,max-heapify讓a[i]在最大堆中「下降」,使以i為根的子樹成為最大堆。具體的偽**如下所示:

max_heapify(a,i)

1 l ← left(i)

2 r ← right(i)

3if l <= heap-size[a] and

a[l] > a[i]

4 then largest ← l

5else largest ← i

6if r <= heap-size[a] and

a[r] > a[largest]

7 then largest ← r

8if largest ≠ i

9 then exchange a[i] ‹-› a[largest]

10 max_heapify(a,largest)

下圖描述了max_heapify的過程:

在演算法中,每一步,從元素a[i],a[left(i)]和a[right(i)]中找出最大的,將下標存在largest中,如果a[i]是最大的,即i=largest,則以i為根的子樹已經是最大堆,如果a[i]不是最大的,則交換a[i]與a[largest],將i「下降」,堆該子樹遞迴呼叫max-heapify。

當max-heapify作用在一棵以結點i為根、大小為n的子樹上時,其執行時間為調整元素a[i]、a[left(i)]和a[right(i)]的關係所用時間為θ(1)的常數時間,再加上對以i的某個子節點為根的子樹遞迴呼叫max-heapify所需的時間。i結點的子樹大小至多為2n/3(最壞情況發生在底層恰好半滿的時候),那麼max-heapify的執行時間如下:t(

n)≤t

(2n/

3)+θ

(1) t(n

)≤t(

2n/3

)+θ(

1)根據主定理的情況2,該遞迴式的解為 t(

n)=o

(lgn

) t(n

)=o(

lgn)

可以說,max-heapify作用於乙個高度為h的結點所需的執行時間為o(h)。

有了上述的max-heapify方法來保持堆的性質,我們就可以自底向上的用max-heapify來將乙個陣列a[1,2,…n]變成乙個最大堆。由完全二叉樹的性質可知,子陣列a[(n/2+1)..n]中的元素都是樹中的葉子,因此每個都可以看做是只含乙個元素的堆。過程build-max-heap對樹中每乙個其他結點都呼叫一次max-heapify。具體演算法如下:

build-max-heap(a)

1 heap-size[a] ← length[a]

2 for i ← [length[a]/2] downto 1

3 do max-heapify(a,i)

下圖給出建堆過程的乙個例子:

很簡單的能得到build-max-heap執行時間的乙個上界:每次呼叫max-heapify的時間為o(lgn),共有n次呼叫,故執行時間為o(nlgn)。這個界雖然正確,但是並不緊確,我們可以得到乙個更緊確的界。在樹中,不同高度的結點處執行max-heapify的時間不同,而大部分結點的高度較小。我們可以知道這樣乙個性質:乙個n元素堆的高度為[lgn],並且在任意高度h上,至多有[n/2^(h+1)]個結點,於是我們可以將build-max-heap的代表表達為: ∑h

=0[l

gn][

n2h]

o(h)

=o(n

∑h=0

[lgn

][n2

h]) ∑h=

0[lg

n][n

2h]o

(h)=

o(n∑

h=0[

lgn]

[n2h

])根據公式 ∑k

=0∞k

xk=x

(1−x

)2∑ k=

0∞kx

k=x(

1−x)

2帶入x=1/2,可得 o(

n∑h=

0[lg

n][n

2h])

=o(n

) o(n

∑h=0

[lgn

][n2

h])=

o(n)

開始時,堆排序演算法先用build-max-heap將輸入陣列a[1..n]構造成乙個最大堆,因為陣列中最大元素在根a[1],所以可以通過將a[1]與a[n]互換,此時從堆中「去掉」結點n,可以很容易的將a[1..(n-1)]建造成最大堆。堆排序演算法重複這個過程,堆的大小從n-1降到2,即可獲得最終的排序結果,具體演算法如下:

heapsort(a)

1 build-max-heap(a)

2 for i ← length[a] downto2

3 do exchange a[1] a[i]

4 heap-size[a] ← heap-size[a-1]

5 max-heapify(a,1)

下圖給出乙個排序的例子:

heapsort過程的時間代價為o(nlgn)。其中呼叫build-max-heap的時間為o(n),n-1次max-heapify呼叫中每一次的時間代價為o(lgn)。

資料結構 堆與堆排序

堆其實是從完全二叉樹演變過來的並且用來儲存資料的,什麼是完全二叉樹呢?完全二叉樹就是 若設二叉樹的深度為h,除第h層外,其它各層 1 h 1 的結點數都達到最大個數,第h層所有的結點都連續集中 在最左邊,這就是完全二叉樹。我們知道二叉樹可以用陣列模擬,堆自然也可以。現在讓我們來畫一棵完全二叉樹 從圖...

堆資料結構與堆排序

堆資料結構 堆 二叉堆 是近似的完全二叉樹,最底層允許不滿,堆的相關,陣列長度length,heap size有效長度 堆的有效元素,未排序的數目 heap size堆雖然很像二叉樹,但是我的理解就是用二叉樹的邏輯,但是最主要的區別在,堆的儲存是用陣列,只是用下標來幫助理解 堆的儲存結構 從上往下,...

資料結構與演算法16 堆排序

毫無疑問,排序兩個字沒必要去死磕,這裡的重點,在於排序的方式,堆排序,就是以堆的形式去排序,毫無疑問,了解堆很重要。那麼,什麼是堆呢?這裡,必須引入乙個完全二叉樹的概念,然後過渡到堆的概念。上圖,就是乙個完全二叉樹,其特點在於 從作為第一層的根開始,除了最後一層之外,第n層的元素個數都必須是2的n次...