生活随笔
收集整理的這篇文章主要介紹了
九种排序方法辨析
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
??????雖然網上有很多關于排序的文章,但那畢竟是別人。還是決定自己寫一寫,說不定會發現新的東西。如有錯誤,請務必指正!
??????目前為止學過的排序依次包括:冒泡排序,選擇排序,插入排序,希爾排序,快速排序,歸并排序,堆排序,基數排序,計數排序。
??????注:在每種排序后都注明該排序方法的穩定性。所謂穩定性就是排序前后兩個相同元素的位置是否發生改變。若不變則是穩定排序,改變則是不穩定排序。規定本文中所有排序方式為從小到大排序。
1. 冒泡排序(穩定排序)
??????冒泡排序的思想是比較兩個相鄰元素,若后面的元素更小則交換兩個元素的位置,以此類推。每次迭代過后,當前序列中最大的元素就被置于末尾。 ??????冒泡排序的時間復雜度是O(n2),最好情況是已經有序,時間復雜度降至O(n)。
代碼如下:
Status
Bubble_Sort ( int a
[ MAXSIZE + 1 ] )
{ int i
, j
, temp
; for ( i
= 1 ; i
<= MAXSIZE ; i
++ ) { for ( j
= 1 ; j
<= MAXSIZE - i
; j
++ ) { if ( a
[ j
] > a
[ j
+ 1 ] ) { temp
= a
[ j
] ; a
[ j
] = a
[ j
+ 1 ] ; a
[ j
+ 1 ] = temp
; } } } return OK ;
}
2. 選擇排序(不穩定排序)
??????選擇排序的思想是比較選擇當前序列中最大的元素與序列中最后一個元素做交換。特點是要保留最大元素的位置。 ??????選擇排序的時間復雜度是O(n2),最好情況是已經有序,時間復雜度降至O(n)。 代碼如下:
Status
Choose_Sort ( int a
[ MAXSIZE + 1 ] )
{ int i
, j
, temp
; int minlocation
, minnum
; for ( i
= 1 ; i
<= MAXSIZE ; i
++ ) { minlocation
= i
; minnum
= a
[ i
] ; for ( j
= i
; j
<= MAXSIZE ; j
++ ) { if ( a
[ j
] < minnum
) { minnum
= a
[ j
] ; minlocation
= j
; } } temp
= a
[ minlocation
] ; a
[ minlocation
] = a
[ i
] ; a
[ i
] = temp
; } return OK ;
}
3. 插入排序(穩定排序)
??????插入排序的思想是,把當前元素與之前的每個元素作比較直至出現當前元素大于之前元素的情況。同時在比較過程中,對比較過但不合適的元素右移。在比較前做一個特殊處理:若當前元素小于前一個元素才進入循環作比較,否則說明為當前最大直接放在最后即可。 ??????插入排序的時間復雜度是O(n2),最好情況是已經有序,時間復雜度降低至O(n)。 代碼如下:
Status
Insert_Sort ( int a
[ MAXSIZE + 1 ] )
{ int i
, j
; for ( i
= 2 ; i
<= MAXSIZE ; ++ i
) { if ( a
[ i
] < a
[ i
- 1 ] ) { a
[ 0 ] = a
[ i
] ; a
[ i
] = a
[ i
- 1 ] ; for ( j
= i
- 2 ; a
[ j
] > a
[ 0 ] ; j
-- ) { a
[ j
+ 1 ] = a
[ j
] ; } a
[ j
+ 1 ] = a
[ 0 ] ; } } return OK ;
}
4. 希爾排序(不穩定排序):
??????希爾排序是插入排序的改進算法,調整了每次前移的距離。并在每輪迭代的時候將前移距離減少,最終下降至1。特點是需要提前準備好前移序列。 ??????希爾排序不穩定的原因:一次插入排序是穩定的,但多次使用插入排序可能會改變相同元素的相對位置。所以希爾排序是不穩定排序。 ??????希爾排序的平均時間復雜度是O(nlogn),不過也取決于前移序列的選擇。上網查閱資料后得知,希爾排序的研究還未停止。時間復雜度分布在O(nlogn-n2)。 代碼如下:
Status
Shell_Sort1 ( int a
[ MAXSIZE + 1 ] )
{ int intervel
[ 5 ] = { 9 , 7 , 5 , 3 , 1 } ; for ( int i
= 0 ; i
< 5 ; i
++ ) { Shell_Sort2 ( a
, intervel
[ i
] ) ; } return OK ;
}
Status
Shell_Sort2 ( int a
[ MAXSIZE + 1 ] , int intervel
)
{ int i
, j
; for ( i
= intervel
+ 1 ; i
<= MAXSIZE ; i
++ ) { if ( a
[ i
] < a
[ i
- intervel
] ) { a
[ 0 ] = a
[ i
] ; for ( j
= i
- intervel
; j
> 0 && a
[ j
] > a
[ 0 ] ; j
-= intervel
) { a
[ j
+ intervel
] = a
[ j
] ; } a
[ j
+ intervel
] = a
[ 0 ] ; } } return OK ;
}
5. 快速排序(不穩定排序):
?????? 快速排序的思想是遞歸選取一個中心點(常選取中心點是序列的第一個元素),將小于中心的值移動到左邊,將大于中心的值移動到右邊。 ??????快速排序的平均時間復雜度是O(nlogn),影響時間復雜度的是中心點的選取,即左右兩邊的比重。最壞情況是選取一邊沒有元素的中心點,這樣時間復雜度下降至O(n2)。最優情況是選取得中心點恰好是序列的中心點,時間復雜度為O(nlogn)??勺C當選取中心點出現左邊1個,右邊n個的情況時間復雜度也為O(nlogn)。說明快速排序的壞情況是間隔出現的有兩種方式可以解決這個問題:1.不選取序列第一個元素,改為隨機選取。2.打亂當前序列。經過驗證第二種方式的實際效果更好。可以使用隨機化算法,先將序列打亂順序再使用快速排序。這樣時間復雜度可穩定在O(nlogn)。 代碼如下:
Status
Quick_Sort ( int a
[ MAXSIZE + 1 ] , int low
, int high
)
{ int tag
; if ( low
< high
) { tag
= Tag ( a
, low
, high
) ; Quick_Sort ( a
, low
, tag
- 1 ) ; Quick_Sort ( a
, tag
+ 1 , high
) ; } return OK ;
}
int
Tag ( int a
[ MAXSIZE + 1 ] , int low
, int high
)
{ int tag
; tag
= a
[ low
] ; while ( low
< high
) { while ( low
< high
&& a
[ high
] >= tag
) { high
-- ; } a
[ low
] = a
[ high
] ; while ( low
< high
&& a
[ low
] <= tag
) { low
++ ; } a
[ high
] = a
[ low
] ; } a
[ low
] = tag
; return low
;
}
6. 歸并排序(穩定排序):
??????歸并排序的思想是將序列遞歸分治,分解至最小模塊后。不斷歸并有序序列,最終得到整體有序。 ??????歸并排序是穩定排序的原因:保持歸并排序的穩定性可做特殊處理,保證兩組有序序列merge時按照前后兩個序列的先后順序插入即可維持穩定。 ??????歸并排序的時間復雜度是O(nlogn)。最好最壞的情況都是O(nlogn)。因為歸并排序層數都是logn向下取整。整體merge都是n。特點是比較穩定,不受輸入序列順序的影響。但需要O(n)的空間復雜度。 代碼如下:
Status
Merge_Insert ( int
* temp_merge
, int
* merge
, int s
, int m
, int t
)
{ int i
, j
, k
; for ( i
= s
, j
= m
+ 1 , k
= s
; i
<= m
&& j
<= t
; ++ k
) { if ( temp_merge
[ i
] < temp_merge
[ j
] ) { merge
[ k
] = temp_merge
[ i
++ ] ; } else { merge
[ k
] = temp_merge
[ j
++ ] ; } } if ( i
> m
) { while ( j
<= t
) { merge
[ k
++ ] = temp_merge
[ j
++ ] ; } } if ( j
> t
) { while ( i
<= m
) { merge
[ k
++ ] = temp_merge
[ i
++ ] ; } }
}
Status
Merge_Sort ( int a
[ MAXSIZE + 1 ] , int
* merge
, int s
, int t
)
{ if ( s
== t
) { merge
[ s
] = a
[ s
] ; } else { int m
; int temp_merge
[ MAXSIZE + 1 ] ; m
= ( s
+ t
) / 2 ; Merge_Sort ( a
, temp_merge
, s
, m
) ; Merge_Sort ( a
, temp_merge
, m
+ 1 , t
) ; Merge_Insert ( temp_merge
, merge
, s
, m
, t
) ; } return OK ;
}
7. 堆排序(不穩定排序):
??????堆排序思想使用堆的數據結構(前面有過專門的文章介紹堆,有需要的朋友可以去看看)。使用最小/大堆,每次將堆頂的元素放到合適的位置。堆排序共有兩步:將堆頂元素放于合適位置->重新將堆調整至最小/大堆。 ??????堆排序的時間復雜度是O(nlogn),堆排序的過程包括構造堆和取出兩步,最好最壞時間復雜度都是O(nlogn)。兩者相差是常數級別。 代碼如下:
Status
Heap_Sort ( int a
[ MAXSIZE + 1 ] )
{ int temp
; for ( int i
= MAXSIZE / 2 ; i
> 0 ; i
-- ) { Heap_Insert ( a
, i
, MAXSIZE ) ; } for ( int i
= MAXSIZE ; i
> 1 ; i
-- ) { temp
= a
[ 1 ] ; a
[ 1 ] = a
[ i
] ; a
[ i
] = temp
; Heap_Insert ( a
, 1 , i
- 1 ) ; } return OK ;
}
Status
Heap_Insert ( int a
[ MAXSIZE + 1 ] , int s
, int m
)
{ int temp
= a
[ s
] ; for ( int j
= 2 * s
; j
<= m
; j
*= 2 ) { if ( j
< m
&& a
[ j
] < a
[ j
+ 1 ] ) { j
++ ; } if ( temp
>= a
[ j
] ) { break ; } a
[ s
] = a
[ j
] ; s
= j
; } a
[ s
] = temp
; return OK ;
}
8. 基數排序(穩定排序)
??????基數排序的思想是從最低位開始排序,依次向前移動且不改變相同值的相對位置。可以使用鏈表來實現,留出10個(0,1,。。。9)空間按照最低位放入空間再連接完成一次迭代。 ??????基數排序的時間復雜度是O(n)。之前很困惑為什么基數排序的時間復雜度比快速排序更低,但是卻沒有快速排序常用。經過查找資料得出原因:基數排序的時間復雜度實際是O(kn)。且常數K的值往往很大,而快速排序的常數值很小。并且基數排序也是相對比較消耗空間的。大多數應用條件下使用快速排序更佳。 代碼如下:
Status
Radix_Sort ( int a
[ MAXSIZE + 1 ] )
{ int temp
; int afterpow
; Queue Ten
[ 10 ] ; for ( int i
= 0 ; i
< 10 ; i
++ ) { InitQueue ( Ten
[ i
] ) ; } for ( int i
= 0 ; i
< 5 ; i
++ ) { for ( int j
= 1 ; j
<= MAXSIZE + 1 ; j
++ ) { afterpow
= pow ( 10 , i
) ; temp
= ( a
[ j
] / afterpow
) % 10 ; EnQueue ( Ten
[ temp
] , a
[ j
] ) ; } for ( int j
= 1 ; j
<= MAXSIZE + 1 ; ) { for ( int i
= 0 ; i
< 10 ; i
++ ) { while ( Ten
[ i
] . rear
!= Ten
[ i
] . front
) { DeQueue ( Ten
[ i
] , temp
) ; a
[ j
++ ] = temp
; } } } for ( int i
= 0 ; i
< 10 ; i
++ ) { Ten
[ i
] . front
= 0 ; Ten
[ i
] . rear
= 0 ; } } return OK ;
}
9. 計數排序(不穩定排序):
??????計數排序的思想是,統計最小元素到最大元素之間每個序列中每個出現的元素個數并制成表。表中包括每個元素開始的位置,是前一項的起始頂點加前一項的個數得到。再遍歷一遍原序列,按照表中位置直接插入指定位置。并動態更新表。 ?????? 計數排序的時間復雜度是O(n),不過計數排序只適用于最大元素不是特別大的情況,原因在于需要保留最小元素到最大元素中每一個元素的個數,有的元素個數為0但仍要占空間。
??????排序方法的選擇更多的是依靠實際序列的特點和規模。對于規模變化大的序列排序,可以使用混合的方法,只要計算出臨界值即可。
??????排序真是編程中最基本的操作之一了,不過這么多方法完全弄懂也是有一定難度的。老師說過:對于排序算法要考慮N無窮的情況,切不可貪圖方便只使用O(n2)的方法啦!
因本文作者水平有限,如有問題,請各位高手在下方評論區指正,謝謝!
總結
以上是生活随笔 為你收集整理的九种排序方法辨析 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。