堆排序 堆排序是利用堆的性質(zhì)進(jìn)行的一種選擇排序。下面先討論一下堆。 1.堆 堆實(shí)際上是一棵完全二叉樹,其任何一非葉節(jié)點(diǎn)滿足性質(zhì): Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]或者Key[i]>=Key[2i+1]&&key>=key[2i+2] 即任何一非葉節(jié)點(diǎn)的關(guān)鍵字不大于或者不小于其左右孩子節(jié)點(diǎn)的關(guān)鍵字。 堆分為大頂堆和小頂堆,滿足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱為大頂堆,滿足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱為小頂堆。由上述性質(zhì)可知大頂堆的堆頂?shù)年P(guān)鍵字肯定是所有關(guān)鍵字中最大的,小頂堆的堆頂?shù)年P(guān)鍵字是所有關(guān)鍵字中最小的。 2.堆排序的思想 利用大頂堆(小頂堆)堆頂記錄的是最大關(guān)鍵字(最小關(guān)鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。 其基本思想為(大頂堆): 1)將初始待排序關(guān)鍵字序列(R1,R2....Rn)構(gòu)建成大頂堆,此堆為初始的無序區(qū); 2)將堆頂元素R[1]與最后一個元素R[n]交換,此時得到新的無序區(qū)(R1,R2,......Rn-1)和新的有序區(qū)(Rn),且滿足R[1,2...n-1]<=R[n]; 3)由于交換后新的堆頂R[1]可能違反堆的性質(zhì),因此需要對當(dāng)前無序區(qū)(R1,R2,......Rn-1)調(diào)整為新堆,然后再次將R[1]與無序區(qū)最后一個元素交換,得到新的無序區(qū)(R1,R2....Rn-2)和新的有序區(qū)(Rn-1,Rn)。不斷重復(fù)此過程直到有序區(qū)的元素個數(shù)為n-1,則整個排序過程完成。 操作過程如下: 1)初始化堆:將R[1..n]構(gòu)造為堆; 2)將當(dāng)前無序區(qū)的堆頂元素R[1]同該區(qū)間的最后一個記錄交換,然后將新的無序區(qū)調(diào)整為新的堆。 因此對于堆排序,最重要的兩個操作就是構(gòu)造初始堆和調(diào)整堆,其實(shí)構(gòu)造初始堆事實(shí)上也是調(diào)整堆的過程,只不過構(gòu)造初始堆是對所有的非葉節(jié)點(diǎn)都進(jìn)行調(diào)整。 下面舉例說明: 給定一個整形數(shù)組a[]={16,7,3,20,17,8},對其進(jìn)行堆排序。 首先根據(jù)該數(shù)組元素構(gòu)建一個完全二叉樹,得到 20和16交換后導(dǎo)致16不滿足堆的性質(zhì),因此需重新調(diào)整 這樣就得到了初始堆。 即每次調(diào)整都是從父節(jié)點(diǎn)、左孩子節(jié)點(diǎn)、右孩子節(jié)點(diǎn)三者中選擇最大者跟父節(jié)點(diǎn)進(jìn)行交換(交換之后可能造成被交換的孩子節(jié)點(diǎn)不滿足堆的性質(zhì),因此每次交換之后要重新對被交換的孩子節(jié)點(diǎn)進(jìn)行調(diào)整)。有了初始堆之后就可以進(jìn)行排序了。
此時3位于堆頂不滿堆的性質(zhì),則需調(diào)整繼續(xù)調(diào)整 這樣整個區(qū)間便已經(jīng)有序了。
從上述過程可知,堆排序其實(shí)也是一種選擇排序,是一種樹形選擇排序。只不過直接選擇排序中,為了從R[1...n]中選擇最大記錄,需比較n-1次,然后從R[1...n-2]中選擇最大記錄需比較n-2次。事實(shí)上這n-2次比較中有很多已經(jīng)在前面的n-1次比較中已經(jīng)做過,而樹形選擇排序恰好利用樹形的特點(diǎn)保存了部分前面的比較結(jié)果,因此可以減少比較次數(shù)。對于n個關(guān)鍵字序列,最壞情況下每個節(jié)點(diǎn)需比較log2(n)次,因此其最壞情況下時間復(fù)雜度為nlogn。堆排序?yàn)椴环€(wěn)定排序,不適合記錄較少的排序。
測試程序
/*堆排序(大頂堆) 2011.9.14*/ |
|