演算法導論 第六章 再談 堆排序和最大優先順序佇列

2021-06-21 20:04:26 字數 4460 閱讀 8971

摘自書本

:這一部分將要給出幾個解決以下排序問題的演算法:

輸入:n 個數的序列( a1,a2,……,an )。

輸出:輸入序列的乙個重排 (a1』,a2』,……,an』 ),使 a1』 <= a2' <= …… <= an' 。

輸入序列通常是乙個 n 元 陣列,但也可能由其他形式來表示,如鍊表。

輸入資料的結構

在實際中,待排序的數很少是孤立的值,他們通常是乙個稱為 記錄 的資料集的一部分。每個記錄有乙個關鍵字 key,它是待排序的值。記錄的其他資料稱為衛星資料,即他們通常以 key 為中心傳送。在乙個排序的演算法中,當交換關鍵字時,衛星資料必也交換。如果記錄都很大,我們可以交換一組指向各個記錄的指標而不是記錄本身,以求將資料移動量減到最小。

在一定的意義上,正是這些實現細節才使得乙個完整的程式不同於演算法。不管我們要排序的是單個的數值還是包含數值的大型記錄,就排序的方法來說他們都是一樣的。因而,為了集中考慮排序問題,我們一般都假設輸入僅由數值構成。將對數字的排序演算法轉換為對記錄排序的程式是很直接的。當然,在具體的工程條件下,實際的程式設計可能還會遇到其他難以捉摸的挑戰。

堆排序

特點:執行時間 為 o(n lgn),原地排序演算法。

(二叉)堆資料結構 是一種陣列物件,它可以被視為一棵完全二叉樹。樹中每個結點與陣列中存放該結點值得那個元素對應。樹的每一層都是填滿的,最後一層可能除外(最後一層從乙個結點的左子樹開始填)。表示堆的 陣列 a 是乙個具有兩個屬性的物件:length[a]是陣列中的元素個數,heap-size[a] 是存放在a中的堆的元素個數。heap-size[a] <= length[a]。樹的根為a[1],給定了某個結點的下標 i,其父節點為 parent(i)、左兒子 left(i)和右兒子 right(i)的下表可以簡單地計算出來:

parent(i): i/2(向前取整)

left(i):2*i

right(i):2*i + 1

/* input:乙個陣列a 和乙個下標 i

當 max-heapify被呼叫時,我們假定以 left(i) 和 right(i)為根的兩顆二叉樹都是最大堆

但這時 a【i】可能小於其子女,這樣就違反了最大堆的性質,max-heapify讓a【i】在最大堆

中下降,以滿足最大堆性質*/

max-heapify( a, i )  堆調整  o(lgn)

1     l = left( i )

2     r = right( i )

3ifl <= heap-size[a]anda[l] > a[i]

4thenlargest = l

5elselargest = i

6ifr <= heap-size[a]anda[r] > a[largest]

7thenlargest = r

8iflargest != i

9thenexchange a[i] <-> a[largest]

10               max-heapify( a, largest )

建堆:子陣列a[((n/2向前取整)+ 1).... n ] 中的元素都是樹中的葉子,因此每個都可以看做只含乙個根元素的堆,故而建堆的過程,可以從 a[ length[a]/2 ] 開始調整 直至 a[1] 。

build-max-heap( a )     建堆     執行時間的界:o(n)

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

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

3          do max-heapify(a,i)

堆排序heap-sort( a )     堆排序 時間代價:o(n lgn)

1     build-max-heap( a )

2     for i = length[a] downto 2

3          do exchange a[1] = a[i]

heap-size[a] = heap-size[a] - 1

max-heapify( a, 1 )

c 語言實現:

#include#includeint heap_size;

//注:本**內,陣列有效值都從 1 開始

int length( int a )

void exchange( int *a, int i, int j)

void max_heapify(int *a, int i ) }

void build_max_heap( int a )

void heap_sort( int *a )

}void main()

; int i;

heap_sort(a);

for(i = 1; i < 6; i++)

printf("%d\t",a[i]);

printf("\n");

}

優先順序佇列

雖然堆排序演算法是乙個很漂亮的演算法,但在實際中,快速排序的乙個好的實現往往優於堆排序。儘管這樣,對資料結構還是有著很大的用處。

乙個堆的很常見的應用:作為高效的優先順序佇列(priority queue)

如堆一樣,佇列也有兩種,最大優先順序佇列和最小優先順序佇列。這裡集中討論 基於最大堆實現的最大優先順序佇列。

優先順序佇列是一種用來維護由一組元素構成的集合s的資料結構,這一組元素的每乙個都有乙個關鍵字key。

最大優先順序佇列的乙個應用: 在一台分時計算機上進行作業排程。這種佇列排程對要執行的各作業及它們之間的相對優先關係加以記錄。當乙個作業做完或被中斷時,用 extract-max 操作從所有等待的作業中,選擇出具有最高優先順序的作業。在任何時候,乙個新作業都可以用 insert 加入到佇列中去。

支援以下操作:

insert( s, x ) //把元素 x 插入到集合 s

maximum( s )     //返回 s 中具有最大關鍵字的元素

extract-max( s )     //去掉並返回 s 中具有最大關鍵字的元素

increase-key( s, x, k )      //將元素 x 的關鍵字的值增加到 k,這裡 k 值不能小於 x 的原關鍵字的值

偽**,最大堆實現最大優先順序佇列

heap-maximum( a )    o(1)

1returna[1]

heap-extract-max( a )     o( lgn )      原堆中去除堆頂最大值,並返回堆頂最大值

1ifheap-size[a] < 1

2thenerror "heap underflow"

3     max = a[1] 

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

5     heap-size[a] = heap-size[a] - 1

6     max-heapify(a,1)

7returnmax    

heap-increase-key( a, i, key )     o(lgn)

1ifkey < a[i]

2thenerror "new key is smaller than current key"

3     a[i] = key

4whilei > 1anda[parent(i)] < a[i]

5doexchange a[i] = a[parent(i)]

6             i = parent( i )

max-heap-insert( a, key )     o(lgn)

1     heap-size[a] = heap-size[a] + 1

2     a[heap-size[a]] = -∞

3     heap-increase-key( a, heap-size[a], key )

**實現類似於 堆排序演算法,再次不做實現。

演算法導論 第六章《堆排序》

本章開始介紹了堆的基本概念,然後引入最大堆和最小堆的概念。全章採用最大堆來介紹堆的操作,兩個重要的操作是調整最大堆和建立最大堆,接著著兩個操作引進了堆排序,最後介紹了採用堆實現優先順序佇列。二叉 堆資料結構是一種陣列物件,它可以被視為一棵完全二叉樹。除了最底層外,該樹是完全充滿的,而且是從左到右填充...

演算法導論 第六章 堆排序

二叉 堆資料結構是一種陣列物件,如下圖所知,他可以被視為一顆完全二叉樹。其有如下性質 1 對於i節點 i表示下標 其父節點為li 2 左孩子節點為2i,右孩子節點為2i 1 2 最大堆滿足 a parent i a i 最小堆滿足 a parent i a i 3 堆的高度為 lgn 4 子陣列元素...

演算法導論第六章 堆排序

堆排序 主要是二叉堆,是乙個陣列,可以近似看作是一棵完全二叉樹。最壞情況執行時間為 n log n 主要性質 1.對於任意乙個下標index,書上寫的是左子女的下標為 2 index,右子女為 2 index 1 但是在實際程式設計中,下標是從0開始的,所以下標的變化應該為 左子女為2 index ...