堆與堆排序

2021-06-20 14:46:26 字數 2317 閱讀 9752

堆排序與快速排序,歸併排序一樣都是時間複雜度為o(n*logn)的幾種常見排序方法。

堆排序是就地排序,輔助空間為o(1)。

它是不穩定的排序方法。(排序的穩定性是指如果在排序的序列中,存在前後相同的兩個元素的話,排序前 和排序後他們的相對位置不發生變化)

先說說什麼是堆,堆通常是乙個可以被看做一棵樹的陣列物件。滿足下列性質:

1.堆中某個節點的值總是不大於或不小於其父節點的值;

2.堆總是一棵完全樹(完全樹就是葉結點僅在層次最大的兩層出現的樹)。

將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。常見的堆有二叉堆、斐波那契堆等。由於其它幾種堆(二項式堆,斐波納契堆等)用的較少,一般將二叉堆就簡稱為堆。

堆的儲存

一般都用陣列來表示堆,i結點的父結點下標就為(i – 1) / 2。它的左右子結點下標分別為2 * i + 1和2 * i + 2。如第0個結點左右子結點下標分別為1和2。

堆的操作

建立堆:

一般情況下,樹並不滿足堆的條件,通過重新排列元素,可以建立一棵」堆化「的樹。如初始表:55 12 16,堆化後為:12 55 16。

堆的插入:

每次插入都是將新資料放在陣列最後。然後樹被更新以恢復堆次序。如初始表:12 22 7 ,插入新資料後,陣列為12 22 7 16,然後重排樹的順序,陣列為12 16 7 22。

可以發現從這個新資料的父結點到根結點必然為乙個有序的數列。

//  新加入i結點  其父結點為(i - 1) / 2

void minheapfixup(int a, int i)

a[i] = temp;

}

//在最小堆中加入新的資料nnum

void minheapaddnumber(int a, int n, int nnum)

堆的刪除

堆中每次都只能刪除第0個資料。為了便於重建堆,實際的操作是將最後乙個資料的值賦給根結點,然後再從根結點開始進行一次從上向下的調整。調整時先在左右兒子結點中找最小的,如果父結點比這個最小的子結點還**明不需要調整了,反之將父結點和它交換後再考慮後面的結點。相當於從根結點將乙個資料的「下沉」過程。

如初始表:12 16 50 22,刪除第0個資料後,陣列為22 16 50 _,然後重排樹的順序,陣列為16 22 50。

//  從i節點開始調整,n為節點總數 從0開始計算 i節點的子節點為 2*i+1, 2*i+2

void minheapfixdown(int a, int i, int n)

a[i] = temp;

}//在最小堆中刪除數

void minheapdeletenumber(int a, int n)

堆化陣列

關於怎樣把乙個資料進行堆化。可能很多人會想,要乙個乙個的從陣列中取出資料來建立堆?不用。

比如說:int a[0] = ;

如果把這個陣列看成是一棵樹,那麼它的葉子結點19,59,64,3,18都分別是乙個合法的堆。只要把49開始向下調整就可以了。然後再取29,16,11,9結點分別作一次向下調整操作就可以了。

//建立最小堆

void makeminheap(int a, int n)

就這樣,堆的操作就全部完成了。

說了這麼多,終於到主角登場了。

根據堆的性質,堆建好之後。堆中第0個資料是堆中最小的資料。取出這個資料再執行下堆的刪除操作。這樣堆中第0個資料又是堆中最小的資料,重複上述步驟直至堆中只有乙個資料時就直接取出這個資料。

由於堆也是用陣列模擬的,故堆化陣列後,第一次將a[0]與a[n - 1]交換,再對a[0…n-2]重新恢復堆。第二次將a[0]與a[n – 2]交換,再對a[0…n - 3]重新恢復堆,重複這樣的操作直到a[0]與a[1]交換。由於每次都是將最小的資料併入到後面的有序區間,故操作完成後整個陣列就有序了。

void minheapsorttodescendarray(int a, int n)

}

注意使用最小堆排序後是遞減陣列,要得到遞增陣列,可以使用最大堆。

應用:堆是一種經典的資料結構,向堆中插入、刪除元素時間複雜度都是 o(lgn), n 為堆中元素的個數,而獲取最小 key 值(小根堆)的複雜度為 o(1)。

libevent中的定時事件管理就是用乙個以時間作為 key 的小根堆結構做的,放棄了原來的紅黑樹,大概就是堆比紅黑樹簡單吧。

參考:

堆與堆排序

二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特性 1 父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值。2 每個結點的左子樹和右子樹都是乙個二叉堆 都是最大堆或最小堆 當父結點的鍵值總是大於或等於任何乙個子節點的鍵值時為最大堆。當父結點的鍵值總是小於或等於任何乙個子節點的鍵值時...

堆與堆排序

堆排序與快速排序,歸併排序一樣都是時間複雜度為o n logn 的幾種常見排序方法。學習堆排序前,先講解下什麼是資料結構中的二叉堆。二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特性 1 父結點的鍵值總是大於或等於 小於或等於 任何乙個子節點的鍵值。2 每個結點的左子樹和右子樹都是乙個二叉堆...

堆與堆排序

堆的儲存 一般都用陣列來表示堆,i結點的父結點下標就為 i 1 2。它的左右子結點下標分別為2 i 1和2 i 2。如第0個結點左右子結點下標分別為1和2。堆排序的思想 利用大頂堆 小頂堆 堆頂記錄的是最大關鍵字 最小關鍵字 這一特性,使得每次從無序中選擇最大記錄 最小記錄 變得簡單。其基本思想為 ...