交換排序的基本思想是:兩兩比較待排序記錄的關鍵字,發現兩個記錄的次序相反時即進行交換,直到沒有反序的記錄為止。
應用交換排序基本思想的主要排序方法有:氣泡排序和快速排序。
氣泡排序
1、排序方法
將被排序的記錄陣列r[1..n]垂直排列,每個記錄r[i]看作是重量為r[i].key的氣泡。根據輕氣泡不能在重氣泡之下的原則,從下往上掃瞄陣列r:凡掃瞄到違反本原則的輕氣泡,就使其向上"飄浮"。如此反覆進行,直到最後任何兩個氣泡都是輕者在上,重者在下為止。
(1)初始
r[1..n]為無序區。
(2)第一趟掃瞄
從無序區底部向上依次比較相鄰的兩個氣泡的重量,若發現輕者在下、重者在上,則交換二者的位置。即依次比較(r[n],r[n-1]),(r[n-1],r[n-2]),…,(r[2],r[1]);對於每對氣泡(r[j+1],r[j]),若r[j+1].key
(3)第二趟掃瞄
掃瞄r[2..n]。掃瞄完畢時,"次輕"的氣泡飄浮到r[2]的位置上……
最後,經過n-1 趟掃瞄可得到有序區r[1..n]
注意:第i趟掃瞄時,r[1..i-1]和r[i..n]分別為當前的有序區和無序區。掃瞄仍是從無序區底部向上直至該區頂部。掃瞄完畢時,該區中最輕氣泡飄浮到頂部位置r[i]上,結果是r[1..i]變為新的有序區。
2、氣泡排序過程示例
對關鍵字序列為49 38 65 97 76 13 27 49的檔案進行氣泡排序的過程【參見動畫演示】
3、排序演算法
(1)分析
因為每一趟排序都使有序區增加了乙個氣泡,在經過n-1趟排序之後,有序區中就有n-1個氣泡,而無序區中氣泡的重量總是大於等於有序區中氣泡的重量,所以整個氣泡排序過程至多需要進行n-1趟排序。
若在某一趟排序中未發現氣泡位置的交換,則說明待排序的無序區中所有氣泡均滿足輕者在上,重者在下的原則,因此,氣泡排序過程可在此趟排序後終止。為此,在下面給出的演算法中,引入乙個布林量exchange,在每趟排序開始前,先將其置為false。若排序過程中發生了交換,則將其置為true。各趟排序結束時檢查exchange,若未曾發生過交換則終止演算法,不再進行下一趟排序。
(2)具體演算法
void bubblesort(seqlist r)
//endwhile
r[i]=pivot; //基準記錄已被最後定位
return i;
} //partition
4、快速排序執行過程
快速排序執行的全過程可用遞迴樹來描述。
分析:
(1)遞迴執行的路線如圖中帶箭頭的包絡線所示。
(2) 遞迴樹上每一結點左旁方括號表示當前待排序的區間,結點內的關鍵字是劃分的基準關鍵字
注意:葉結點對應的子區間只有乙個關鍵字,無須劃分,故葉結點內沒有基準關鍵字
(3) 劃分後得到的左、右兩個子區間分別標在該結點的左、右兩個孩子結點的左邊方括號內。
【例】根結點左旁方括號[49,38,65,97,76,13,27,
49]表示初始待排序的關鍵字,根內的49表示所選的劃分基準記錄的關鍵字,劃分結果是[27,28,13]49[76,97,65,
49_],其左右子區間分別標在根結點的兩個孩子的左邊。
(4) 每個分支結點右旁圓括號中的內容表示對該結點左旁區間的排序過程結束之後返回的結果。它是其左右孩子對應的區間排序完成之後,將左右孩子對應的排序結果分別放在該分支結點的關鍵字前後所得到的關鍵字序列。
【例】分支結點76的左右孩子對應的區間排序後的結果分別是(49_,65)和(97),將它們分別放在76的前後即得(
49,65,76,97),這是對結點76左旁區間[76,97,,65,
49]排序的結果。
(5) 演算法的執行順序是遞迴樹中的箭頭順序,實際上當把劃分操作視為訪問結點的操作時,快速排序的執行過程相當於是先序遍歷其遞迴樹。
注意:任何遞迴演算法均可用遞迴樹來描述其執行過程。
5、快速排序各次劃分後的狀態變化
[49 38 65 97 76 13 27
49] //初始關鍵字
[27 38 13
] 49
[76 97 65
49] //第1次劃分完成之後,對應遞迴樹第2層
[13] 27
[38] 49
[49 65
] 76
[97] //對上一層各無序區劃分完成後,對應遞迴樹第3層
13 27 38 49
49[65
] 76 97 //對上一層各無序區劃分完成後,對應遞迴樹第4層
13 27 38 49
49 65 76 97 //最後的排序結果
6、演算法分析
快速排序的時間主要耗費在劃分操作上,對長度為k的區間進行劃分,共需k-1次關鍵字的比較。
(1)最壞時間複雜度
最壞情況是每次劃分選取的基準都是當前無序區中關鍵字最小(或最大)的記錄,劃分的結果是基準左邊的子區間為空(或右邊的子區間為空),而劃分所得的另乙個非空的子區間中記錄數目,僅僅比劃分前的無序區中記錄個數減少乙個。
因此,快速排序必須做n-1次劃分,第i次劃分開始時區間長度為n-i+1,所需的比較次數為n-i(1≤i≤n-1),故總的比較次數達到最大值:
cmax = n(n-1)/2=o(n2)
如果按上面給出的劃分演算法,每次取當前無序區的第1個記錄為基準,那麼當檔案的記錄已按遞增序(或遞減序)排列時,每次劃分所取的基準就是當前無序區中關鍵字最小(或最大)的記錄,則快速排序所需的比較次數反而最多。
(2) 最好時間複雜度
在最好情況下,每次劃分所取的基準都是當前無序區的"中值"記錄,劃分的結果是基準的左、右兩個無序子區間的長度大致相等。總的關鍵字比較次數:
0(nlgn)
注意:用遞迴樹來分析最好情況下的比較次數更簡單。因為每次劃分後左、右子區間長度大致相等,故遞迴樹的高度為o(lgn),而遞迴樹每一層上各結點所對應的劃分過程中所需要的關鍵字比較次數總和不超過n,故整個排序過程所需要的關鍵字比較總次數c(n)=o(nlgn)。
因為快速排序的記錄移動次數不大於比較的次數,所以快速排序的最壞時間複雜度應為0(n2),最好時間複雜度為o(nlgn)。
(3)基準關鍵字的選取
在當前無序區中選取劃分的基準關鍵字是決定演算法效能的關鍵。
①"三者取中"的規則
"三者取中"規則,即在當前區間裡,將該區間首、尾和中間位置上的關鍵字比較,取三者之中值所對應的記錄作為基準,在劃分開始前將該基準記錄和該區伺的第1個記錄進行交換,此後的劃分過程與上面所給的partition演算法完全相同。
②取位於low和high之間的隨機數k(low≤k≤high),用r[k]作為基準
選取基準最好的方法是用乙個隨機函式產生乙個取位於low和high之間的隨機數k(low≤k≤high),用r[k]作為基準,這相當於強迫r[low..high]中的記錄是隨機分布的。用此方法所得到的快速排序一般稱為隨機的快速排序。具體演算法【參見教材】
注意:隨機化的快速排序與一般的快速排序演算法差別很小。但隨機化後,演算法的效能大大地提高了,尤其是對初始有序的檔案,一般不可能導致最壞情況的發生。演算法的隨機化不僅僅適用於快速排序,也適用於其它需要資料隨機分布的演算法。
(4)平均時間複雜度
儘管快速排序的最壞時間為o(n2),但就平均效能而言,它是基於關鍵字比較的內部排序演算法中速度最快者,快速排序亦因此而得名。它的平均時間複雜度為o(nlgn)。
(5)空間複雜度
快速排序在系統內部需要乙個棧來實現遞迴。若每次劃分較為均勻,則其遞迴樹的高度為o(lgn),故遞迴後需棧空間為o(lgn)。最壞情況下,遞迴樹的高度為o(n),所需的棧空間為o(n)。
(6)穩定性
快速排序是非穩定的,例如[2,2,1]。
常見的排序演算法 交換排序
交換排序 利用交換元素的位置進行排序的方法稱作交換排序。常見的交換排序的方法 氣泡排序和快速排序。氣泡排序 基本思想 如下void bubblesort int array,int size 總結 快速排序 基本思想 快速排序是hoare於1962年提出的一種二叉樹結構的交換排序方法,其基本思想為 ...
常見排序演算法的實現 交換排序
交換排序 交換排序的分類 氣泡排序 快速排序 1.氣泡排序 思想 從陣列的末尾元素開始比較兩個元素的大小,然後根據大小換位置,直到將最大或最小的元素排到陣列的起始位置 實現如下 void swap int left,int right void bubblesort int arr,int size...
排序演算法 交換排序
交換排序的基本思想 兩兩比較待排序元素的關鍵字,發現兩個元素的次序相反時則進行交換,直到沒有反序的元素為止。1.氣泡排序 交換排序 演算法 氣泡排序 bubble sort 輸入 待排序元素的陣列,待排序元素個數 輸出 原理 通過無序區中相鄰元素間關鍵字的比較和位置的交換,使關鍵字最小的元素如氣泡一...