算法导论笔记:06堆排序
? ? ? ?滿二叉樹:除最后一層無任何子節(jié)點外,每一層上的所有結(jié)點都有兩個子結(jié)點(也可以這樣理解,除葉子節(jié)點外的所有結(jié)點均有兩個子結(jié)點。節(jié)點數(shù)達(dá)到最大值。所有葉子結(jié)點必須在同一層上)
?
? ? ? ?1:堆排序的時間復(fù)雜度為O(nlgn)。具有空間原址性的特點,也就是任意時刻只需常數(shù)個額外元素空間存儲臨時數(shù)據(jù)。
?
? ? ? ?2:堆是一顆近似完全二叉樹:除了最底層外,該樹是完全充滿的,最底層是從左向右填充的。
?
? ? ? ?3:堆可以用數(shù)組存儲,對于元素下標(biāo)i,可以很快得出它的父節(jié)點,左孩子,右孩子的下標(biāo)(i從0開始):
PARENT(i) = (i-1)/2? ?
LEFT(i) = 2i + 1 ? ?
RIGHT(i) = 2i + 2 ? ?
?
?????? 一般而言,上述計算父母,左孩子,右孩子索引的函數(shù)都是通過宏或者內(nèi)聯(lián)函數(shù)實現(xiàn)。
?
? ? ???4:堆有兩種:最大堆和最小堆。
?????? 最大堆是:除了根節(jié)點之外,所有節(jié)點i都要滿足:A[PARENT(i)]?>=?A[i]。即某個節(jié)點的值最多與父節(jié)點一樣大。因此,最大堆中的最大元素放在根節(jié)點中,任意一個子樹中所包含的所有節(jié)點的值都小于等于子樹根節(jié)點的值。如果是從小到大排序,一般用最大堆。
?????? 最小堆是:除了根節(jié)點之外,所有節(jié)點i都要滿足:A[PARENT(i)] <= A[i]。最小堆的最小值放在根節(jié)點中。如果是從大到小排序,用最小堆。
?
? ? ???5:一個包含n個元素的堆,高度為lgn 。堆結(jié)構(gòu)上的一些基本操作的運(yùn)行時間與堆高度成正比,所以時間復(fù)雜度為O(lg n)。
?
? ? ???6:當(dāng)用數(shù)組表示n個元素的堆時,葉節(jié)點的下標(biāo)分別是:((n-2)/2)+1, ((n-2)/2)+2,……,n。(默認(rèn)元素下標(biāo)從0開始,所以,最后一個結(jié)點的父節(jié)點一定是最后一個內(nèi)部結(jié)點。)
?
? ? ? ?7:最大堆上的操作有:
?????? MAX-HEAPIFY???? :維護(hù)最大堆的性質(zhì),時間復(fù)雜度為O(lg n)。
?????? BUILD-MAX-HEAP:從無序的數(shù)組構(gòu)造一個最大堆,時間復(fù)雜度為O(n)。
?????? HEAPSORT: 對數(shù)組進(jìn)行原址排序,時間復(fù)雜度為O(nlg n)
?????? MAX-HEAP-INSERT, HEAP-EXTRACT-MAX, ?HEAP-INCREASE-KEY, ?HEAP-MAXIMUM
??????
? ? ? ?8:MAX-HEAPIFY:維護(hù)最大堆性質(zhì),輸入為數(shù)組A和下標(biāo)i。調(diào)用MAX-HEAPIFY時,始終假定以LEFT(i)和RIGHT(i)為根的左右子樹都已經(jīng)是最大堆了。MAX-HEAPIFY通過讓A[i]的值在最大堆中“逐級下降”,從而使得以A[i]為根節(jié)點的子樹成為最大堆。
?????? 該算法中,每一步都是從A[i],RIGHT[i],LEFT[i]這三者中選出最大值。如果A[i]是最大值,則算法結(jié)束,否則,將A[i]與最大值A(chǔ)[j](j= RIGHT[i] or LEFT[i])交換,從而使i及其孩子滿足最大堆的性質(zhì)。然后針對j再次調(diào)用MAX-HEAPIFY過程,直到堆的末尾。代碼如下:
//from up to down to keep the property of max ?heap (recursion)
void ?maxheapify(int ?*set, ?int ?index, ?int ?size)
{
? ? ???int ?left = LEFT(index);
? ? ???int ?right = RIGHT(index);
? ? ???int ?largest;
? ? ???if(left < size && set[index] < set[left])
? ? ???{
? ? ???? ? ???largest = left;
? ? ???}
? ? ???else
? ? ???{
? ? ???? ? ???largest = index;
? ? ???}
? ? ???if(right < size && set[largest] < set[right])
? ? ???{
? ? ???? ? ???largest = right;
? ? ???}
? ? ???if(largest != index)
? ? ???{
? ? ???? ? ???exchange(set+index, set+largest);
? ? ???? ? ???maxheapify(set, largest, size);
? ? ???}
}
//from up to down to keep the property of max ?heap (loop)
void maxheapify2(int *set, int index, int size)
{
? ? ???int left;
? ? ???int right;
? ? ???int largest;
? ? ???while(index < size)
? ? ???{
? ? ???? ? ???left = LEFT(index);
? ? ???? ? ???right = RIGHT(index);
? ? ???? ? ???if(left < size && set[index] < set[left])
? ? ???? ? ???{
? ? ???? ? ???? ? ???largest = left;
? ? ???? ? ???}
? ? ???? ? ???else
? ? ???? ? ???{
? ? ???? ? ???? ? ???largest = index;
? ? ???? ? ???}
? ? ???? ? ???if(right < size && set[largest] < set[right])
? ? ???? ? ???{
? ? ???? ? ???? ? ???largest = right;
? ? ???? ? ???}
? ? ???? ? ???if(largest != index)
? ? ???? ? ???{
? ? ???? ? ???? ? ???exchange(set + index, set + largest);
? ? ???? ? ???? ? ???index = largest;
? ? ???? ? ???}
? ? ???? ? ???else
? ? ???? ? ???{
? ? ???? ? ???? ? ???return;
? ? ???? ? ???}
? ? ???}
}
?
?????? 若以i為根節(jié)點的子樹元素個數(shù)為n,則其左孩子結(jié)點的個數(shù)最多為2/3 * n。證明如下:假設(shè)左子樹節(jié)點數(shù)為x,右子樹節(jié)點數(shù)為y,所以n = x + y + 1。因堆是從左向右填充的,所以x >= y。如果要使x達(dá)到最大值,那么這種情況就是堆的最底層元素恰好半滿。
?????? 假設(shè)堆的高度為h,那么左子樹高度為h-1,右子樹高度為h-2。所以,n <= 2^(h+1) - 1,
x = 2^h - 1, y = 2^(h-1) - 1。所以 x/y ?≈ 2/1 ?。所以,x ≈ 2n/3 。
?????? 所以,遞歸的算法:T(n) = T(2n/3) + Θ(1)。從而T(n) = O(lg n)。
?
? ? ???9:因為MAX-HEAPIFY(A,i)包含一個假設(shè),就是i的左右子樹都已經(jīng)是最大堆了,所以一般情況下,都是自底向上的調(diào)用MAX-HEAPIFY,比如建堆函數(shù)BUILD-MAX-HEAP,該算法把一個大小為n的數(shù)組轉(zhuǎn)換為最大堆。因為葉子節(jié)點可以認(rèn)為已經(jīng)是最大堆了,所以建堆的過程從最后一個內(nèi)部結(jié)點開始,也就是下標(biāo)為(size-2)/2的元素。從該節(jié)點開始,依次向前調(diào)用MAX-HEAPIFY,建立最大堆。
?????? 在BUILD-MAX-HEAP中,需要調(diào)用n次MAX-HEAPIFY過程,所以錯略計算該算法的時間復(fù)雜度為O(nlg n)。這不是一個緊缺的上界,因為MAX-HEAPIFY的時間跟高度h有關(guān),h的范圍是[0,lg n],而且高度為h的元素個數(shù)最多為n/2^(h+1) 。所以,實際上BUILD-MAX-HEAP的時間復(fù)雜度為O(n)。代碼如下:
?//build the max heap from set with call maxheapify
void buildmaxheap(int *set, int size)
{
? ? ???int index = size-1;
? ? ???int parent = PARENT(index);
? ? ???int i;
? ? ???for(i = parent; i >= 0; i--)
? ? ???{
? ? ???? ? ???maxheapify2(set, i, size);
? ? ???}
}
? ? ???10:利用最大堆進(jìn)行排序的算法HEAPSORT的基本思想是:
?????? 首相利用BUILD-MAX-HEAP算法將一個n元素數(shù)組轉(zhuǎn)換為最大堆,此時該數(shù)組的最大元素就是A[0],所以,交換A[0]和A[n-1]。
?????? 然后,將數(shù)組A[0…n-2]看成新的數(shù)組,該數(shù)組中,除了根節(jié)點之外,其他左右子樹依然滿足最大堆的性質(zhì),只是根節(jié)點因為發(fā)生了交換所以有可能不滿足最大堆性質(zhì),所以,對根節(jié)點調(diào)用MAX-HEAPIFY即可。
?????? 重復(fù)以上這個過程直到堆的大小變?yōu)?。
?????? 該算法的時間復(fù)雜度為O(n lg n)。代碼如下:
//sort use max heap
void maxheapsort(int *heap, int size)
{
? ? ???int heapsize = size;
? ? ???int i;
? ? ???buildmaxheap(heap, size);
? ? ???for(i = size - 1; i > 0; i--)
? ? ???{
? ? ???? ? ???exchange(heap, heap+i);
? ? ???? ? ???heapsize--;
? ? ???? ? ???maxheapify2(heap, 0, heapsize);
? ? ???}
}
?
11:優(yōu)先隊列
?????? 堆排序是一個優(yōu)秀的算法,但是在實際應(yīng)用中,快速排序一般會快于堆排序。堆還可以作為優(yōu)先隊列的實現(xiàn)。最大堆和最小堆分別對應(yīng)于最大優(yōu)先隊列和最小優(yōu)先隊列。優(yōu)先隊列是一個集合,集合中的元素都有一個關(guān)鍵字(key),此關(guān)鍵字標(biāo)識該元素的優(yōu)先級。最大優(yōu)先隊列的例子是進(jìn)程調(diào)度,每次調(diào)度進(jìn)程時,都是從隊列中選擇優(yōu)先級最高的進(jìn)程進(jìn)行運(yùn)行。最小優(yōu)先隊列的例子是事件驅(qū)動的模擬器,每個事件以發(fā)生時間為關(guān)鍵字,每次選擇到時的事件進(jìn)行模擬。
?????? 優(yōu)先隊列支持的操作有(優(yōu)先隊列可用堆來實現(xiàn),下面的優(yōu)先隊列的操作實際上是針對最大堆的):
?????? INSERT:向優(yōu)先隊列中插入元素。
?????? MAXIMUM:返回優(yōu)先隊列中優(yōu)先級最大的元素。
?????? EXTRACT-MAX:返回優(yōu)先級最大的元素,并且將鈣元素刪除。
?????? INCREASE-KEY:將某個元素的關(guān)鍵字增加為k,重新構(gòu)造優(yōu)先隊列。
?????? DELETE:從優(yōu)先隊列中刪除元素。
?
12:EXTRACT-MAX:返回優(yōu)先級最大的元素,并且將該元素刪除。
???? 該算法將A[1]記錄,然后將最后的元素A[heapsize]存儲到A[1]中,然后減少heapsize,然后調(diào)用MAXHEAPIFY重新調(diào)整最大堆。最后返回記錄的A[1]的值。該算法的時間復(fù)雜度與MAXHEAPIFY相同,為O(lg n)。代碼如下:
//return the max elements and del it
int heap_extractmax(int *set, int size)
{
? ? ???if(size < 1)
? ? ???{
? ? ???? ? ???printf("heap size error\n");
? ? ???? ? ???return -1;
? ? ???}
? ? ???int max = set[0];
? ? ???int heapsize = size;
? ? ???set[0] = set[size-1];
? ? ???set[size-1] = NEINFINITE;
? ? ???heapsize = size - 1;
? ? ???maxheapify(set,0,heapsize);
? ? ???return max;
}
?
? ? ???14:INCREASE-KEY:將某個元素的關(guān)鍵字增加為key,重新構(gòu)造優(yōu)先隊列。
?? ? ???該算法將A[i]的關(guān)鍵字置為key,然后從索引i開始向上比較,如果A[i] > A[PARENT(i)],則交換A[i] 和 A[PARENT(i)],然后i= PARENT(i)。重復(fù)這個過程直到I = 1。
?????? 該算法與MAXHEAPIFY類似,只不過該算法是從下往上維護(hù)最大堆的性質(zhì)。該算法的時間復(fù)雜度為O(lg n)。代碼如下:
//from down to up to keep the property of max heap
void heap_increasekey(int *set, int index, int key, int size)
{
? ? ???int i, p;
? ? ???if(set[index] > key)
? ? ???{
? ? ???? ? ???printf("current is %d, key is %d, error\n", set[index], key);
? ? ???? ? ???return ;
? ? ???}
? ? ???set[index] = key;
? ? ???i = index;
? ? ???while(i > 0)
? ? ???{
? ? ???? ? ???p = PARENT(i);
? ? ???? ? ???if(key > set[p])
? ? ???? ? ???{
? ? ???? ? ???? ? ???set[i] = set[p];
? ? ???? ? ???? ? ???i = p;
? ? ???? ? ???}
? ? ???? ? ???else
? ? ???? ? ???{
? ? ???? ? ???? ? ???set[i] = key;
? ? ???? ? ???? ? ???break;
? ? ???? ? ???}
? ? ???}
}
?
? ? ???15:INSERT:向優(yōu)先隊列中插入元素。
?????? 該算法首先將heapsiize增加,然后置A[heapsize]為 。然后調(diào)用INCREASE-KEY(set, heapsize, key)即可,該算法的時間復(fù)雜度為O(lg n)。代碼如下:
//insert elements to max heap
void maxheapinsert(int *set, int key, int heapsize, int size)
{
? ? ???if(heapsize >= size)
? ? ???{
? ? ???? ? ???printf("heapsize larger than size, error\n");
? ? ???? ? ???return;
? ? ???}
? ? ???heapsize ++;
? ? ???set[heapsize - 1] = NEINFINITE;
? ? ???heap_increasekey(set, heapsize-1, key, heapsize);
}
?
? ? ???16:DELETE:從優(yōu)先隊列中刪除元素。
?????? 該算法將最后的元素A[heapsize]存儲到A[index]中,然后減少heapsize。然后比較當(dāng)前index元素的值與父母的值大小,如果A[index] < A[PARENT(index)],則從上往下維護(hù)堆的性質(zhì)(MAXHEAPIFY),否則,從下往上維護(hù)堆的性質(zhì)(INCREASE-KEY)。代碼如下:
//del elements from max heap,note: it should be up to down or down to up
void maxheapdel(int *set, int index, int size)
{
? ? ???int heapsize = size-1;
? ? ???int p = PARENT(index);
? ? ???int key = set[size-1];
? ? ???if(key < set[p])
? ? ???{//down
? ? ???? ? ???set[index] = set[size-1];
? ? ???? ? ???maxheapify(set, index, heapsize);
}
? ? ???else
? ? ???{//up
? ? ???? ? ???heap_increasekey(set,index,key,heapsize);
? ? ???}
}?
? ? ???17:設(shè)計一個復(fù)雜度為O(nlg k)的算法,將k個有序鏈表合并為一個有序鏈表,其中n為所有鏈表元素的總和。
?????? 首先將所有k個鏈表的第一個元素組成一個最小堆,然后EXTRACT-MIN得到綜合鏈表的第一個元素,然后將MIN所在鏈表的第二個元素INSERT到最小堆中,然后再次EXTRACT-MIN。如此重復(fù)下去,知道最小堆為空。該算法時間復(fù)雜度為O(nlg k)。
?
18:一個m * n的YOUNG氏矩陣中,每一行的數(shù)據(jù)都是從左向右進(jìn)行排序的,每一列的元素都是從上到下排序的,如果矩陣中某個元素不存在,則記該元素為 比如包含元素{9,6,3,2,4,8,5,14,12}的4 x 4的Young氏矩陣如下:
2 3 4 5
6 8 9 12
14 ∞ ∞ ∞
∞ ∞ ∞ ∞
? ? ???a:給出一個O(m + n)的EXTRACT-MIN算法。
?????? 類似于堆的EXTRACT-MIN算法,首先將A[1][1]記錄下來以備返回,然后將矩陣的最后一個元素A[m][n]存儲到A[1][1]中,然后比較A[1][1]。A[1][2]和A[2][1]的值,將其中的最小值與A[1][1]進(jìn)行對換,然后再次調(diào)用該過程。每次調(diào)用都是比較A[i][j]、A[i][j+1]和A[i+1][j]的值,將最小值記錄到A[i][j]中,然后針對(I,j+1)或者(j+1, i)再次調(diào)用該過程(類似于MINHEAPIFY)。可見該過程的時間復(fù)雜度為O(m+n)。
?
? ? ???b:設(shè)計一個O(m + n)的算法,確定某個數(shù)是否在該矩陣中。
?????? 每次通過將X與矩陣中最右上角的元素進(jìn)行比較,如果X大,則說明X可能在剩下的(m-1) * n的矩陣中,如果X小,說明X有可能在剩下的m * (n-1)矩陣中。該算法的時間復(fù)雜度為O(m + n)。
?
轉(zhuǎn)載于:https://www.cnblogs.com/gqtcgq/p/7247241.html
總結(jié)
以上是生活随笔為你收集整理的算法导论笔记:06堆排序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: LInux 安全测试 2
- 下一篇: Michael-Scott非阻塞队列(l