堆是具有下列性質的完全二叉樹:每個節點的值都大於或等於其左右孩子節點的值,稱為大頂堆;或者每個節點的值都小於或等於其左右孩子的值,稱為小頂堆。如下圖舉例:
通過堆的定義可知,根節點一定是對中所有節點的最大(小)值。較大(小)的節點靠近根節點(並不絕對,比如上圖小頂堆中60, 40均小於70,但它並沒有70靠近根節點)
按層序方式給節點從1開始編號,則節點之間滿足下列關係:
k_i \geq k_\\ k_i \geq k_ \end\right.
k_i \leq k_\\ k_i \leq k_ \end\right.
2n這裡i為什麼小於等於 n
2\frac
2n呢?
根據完全二叉樹的性質,對於一棵完全二叉樹,如果某乙個節點的下標i=1,則這個節點是二叉樹的根,無雙親;如果i > 1,則其雙親是節點i
2\frac
2i。那麼對於有n個節點的二叉樹來說,它的i值自然就是小於等於n
2\frac
2n了。
堆排序(heap sort)就是利用堆(這裡我們假設利用的最大堆,最小堆類似)進行排序的方法,它的基本思想就是,將待排序的序列構造成乙個大頂堆。此時整個序列的最大值就是堆頂的根節點。將它移走(其實就是將其與堆陣列的末尾元素交換,此時末尾元素就是最大值),然後將剩餘的n-1個序列重新構造成乙個堆,這樣就會得到n個元素中次大值,如此反覆執行,就能得到乙個有序序列。
演算法流程:
將含有n個元素的待排序的序列 (r
1r_1
r1, r
2r_2
r2, r
3r_3
r3, …, r
nr_n
rn) 初始化成乙個大頂堆,此堆為初始的無序區。
將堆頂元素r[1]和最後乙個元素r[n]進行交換,交換完成後得到新的無序區:(r
1r_1
r1, r
2r_2
r2, r
3r_3
r3, …, rn−
1r_rn−1
),和乙個新的有序區 (r
nr_n
rn)。
交換後的新的堆頂元素r[1]可能破壞堆的性質,所以重新將當前無序區調整為乙個新堆。然後再將堆頂元素r[1]和無序區最後乙個元素進行交換,得到乙個新的無序區 (r
1r_1
r1, r
2r_2
r2, …, rn−
2r_rn−2
)和乙個新的有序區 (rn−
1r_rn−1
, r
nr_n
rn)。
重複上述過程,直到有序區的元素個數為n-1,整個排序過程完成
舉例說明:
假設我們要排序的序列是,長度為9
將待排序序列初始化為乙個堆:
則堆排序過程如下:
(ps:畫圖一度畫瘋…)
現在我們了解了堆排序的過程,但是還有兩個問題:
如何**乙個無序序列構建成乙個堆?
如何在輸出堆頂元素後,調整剩餘元素成為乙個新的堆?
我們所謂的將待排序的序列構造成乙個大頂堆,其實就是從下往上、從右到左,將每個非終端(非葉子節點)節點當做根節點,將其和其子樹調整成最大堆。
具體看**:
public
class
maxheapsort
if(temp >= elem[j]
) elem[s]
= elem[j]
; s = j;}
elem[s]
= temp;
//插入
}public
void
swap
(int
elem,
int i,
int j)
/** * 對順序表elem進行堆排序
* @param elem
*/public
void
maxheapsort
(int
elem)
for(
int i = length; i >
1; i--)}
public
static
void
main
(string[
] args)
; m.
maxheapsort
(elem)
;for
(int i =
1; i < elem.length; i++)}
}
輸出:
10, 20, 30, 40, 50, 60, 70, 80, 90,
堆排序的執行時間主要是消耗在初始構建堆和重建堆時的反覆篩選上。
在構建堆的過程中,因為我們是完全二叉樹從最下層最右邊的非終端節點開始構建,將它和其孩子進行比較和若有必要的交換,對於每個非終端節點來說,其實最多進行兩次比較和互換操作,因此真個構建堆的時間複雜度是o(n)。
在正式排序時,第i次取堆頂記錄重建堆需要用o(logi)的時間 (完全二叉樹的某個節點到根節點的距離是[log
2log_2
log2
i] + 1),並且需要取n-1次堆頂記錄,因此重建堆的時間複雜度為o(nlog
\log
logn)。
所以堆排序的整體時間複雜度為o(nlog
\log
logn)。由於堆排序對原始記錄的排序狀態並不敏感,因此無論是最好,最壞和平均時間複雜度均為o(nlog
\log
logn)。
空間複雜度上,只有乙個用來交換的暫存單元,但是由於記錄的比較與交換是跳躍式進行的,因此堆排序也是一種不穩定的排序方法
另外,由於初始構建堆所需的比較次數比較多,因此他不適合待排序序列個數較少的情況。
實現最大堆和堆排序
最大堆 最大堆 堆中某個節點的值總是不大於其父節點的值,可以使用陣列實現 按層,從上到下,從左到有,為節點序號i 往堆中增加元素的操作 往陣列末尾增加,如果比父節點大,則交換,同理,一直到根節點 該操作稱為siftup 去除最大堆根元素的操作 將根元素去除,將最後乙個元素放在根元素的位置,比較根元素...
最大堆實現堆排序
堆排序 這裡使用的是最大堆 思想 1 將當前的堆轉換成最大堆 從最大的非葉子結點開始,1 判斷根結點和左右結點的大小交換相互的位置,使得該子樹成為最大堆,每次交換成功後就繼續往該結點的子結點去重複 1 操作,直到根結點後再去下乙個非葉子結點,直到根結點 2 轉成最大堆後,每次就將第乙個結點互最後乙個...
最大堆 排序
解除安裝最前面,下面的所有討論都是基於二叉堆 一 什麼是堆 堆是乙個陣列結構,可以看著為一顆完全二叉樹,把這顆完全二叉樹按層從上到下,每層從左至右編序號,每個序號所對應的元素即為陣列中該序號的元素 該樹出最後一層以外每一層都排滿,最後一層從左至右,先左孩子再右孩子排列,如果有父節點沒有排滿孩子 無孩...