-快速排序
算法概述:與歸并相似,分而治之
void Quicksort ( ElementType A[ ] , int N )
{
if ( n < 2 ) return ;
pivot = 從A[ ] 中選出一個主元;
將 S={ A[ ] \ pivot } 分成2個獨立子集:
A1 = { a屬于S | a <= pivot } 和
A3 = { a屬于S | a >= pivot } ;
A[ ] = Quicksort( A1 , N1 )并 { pivot }并Quicksort( A2 , N2 ) ;
}
選主元:
取中位數
ElementType Median3 ( ElementType A[ ] , int Left , int Right )
{
int Center = ( Left + Right ) / 2 ;
if ( A[ Left ] > A[ Center ] )
Swap ( &A[ Left ] ) , &A[ Center ] ) ;
if ( A[ Left ] > A[ Right ] )
Swap ( &A[ Left ] ) , &A[ Right ] ) ;
if ( A[ Center ] > A[ Right ] )
Swap ( &A[ Center ] ) , &A[ Right ] ) ;
Swap ( &A[ Center ] , &A[ Right - 1 ] ) ; // 將pivot藏到右邊
return A[ Right - 1 ] ; // 返回主元,它在Right - 1這個位置
}
子集劃分
每次選定主元并完成子集劃分后,主元插入的位置都是它的最終位置,它一次性放到了正確位置以后都不用移動
如果有元素正好等于pivot,停下來交換?不交換,繼續移動指針?比較后選擇交換
快速排序問題:該算法用到遞歸,對于小規模數據(比如小于100的數據)可能還不如插入排序快
所以,當遞歸的數據規模充分小,則停止遞歸,直接調用簡單排序;在程序中定義一個Cutoff的閾值,小于該值則調用簡單算法
算法實現:
void Quicksort ( ElementType A[ ] , int Left , int Right )
{
if ( Cutoff <= Right - Left ) {
Pivot = Median3 ( A , Left , Right ) ;
i = Left ; j = Right - 1 ; // 開始做子集劃分
for ( ; ; ) {
while ( A[ ++ i ] < Pivot ) { }
while ( A[ - - j ] > Pivot ) { }
if ( i < j )
Swap ( &A[ i ] , &A[ j ] ) ;
else break ; // 子集劃分結束
}
Swap ( &A[ i ] , &A[ Right - 1 ] ) ;
Quicksort ( A , Left , i - 1 ) ;
Quicksort ( A , i + 1 , Right ) ;
}
else
Insertion_Sort ( A + Left , Right - Left + 1 ) ; // 當超過Cutoff閾值,開始用簡單排序
}
統一接口:
void Quick_Sort ( ElementType A[ ] , int N )
{
Quicksort( A , 0 , N - 1 ) ;
}
-表排序
待排元素不是簡單的數字而是比較大的結構,移動元素步驟不能忽略不計
排序過程中不需要移動原始數據,而是移動指向它們的指針,這樣的排序叫間接排序;
定義一個指針數組作為“表”(table),如果僅要求按順序輸出,則輸出:
A[ table[0] ], A[ table[1] ], …… , A[ table[N-1] ]
物理排序:
N個數字的排列由若干個獨立的環組成
將物理排序獨立成若干個獨立的操作,開辟一個臨時空間,從環中取出一個元素暫時放入該空間,依次將該環中其他元素 一一放置到正確的位置,最后將取出的元素拿出放進環中最后空出來的位置。
最好情況:初始即有序
最壞情況:有N/2個環,每個環包含2個元素,這是需要3N/2次元素移動。(每移動一對元素要走三步)
-基數排序
桶排序 eg:有N個學生,他們的成績是0到100之間的整數,如何在線性時間內將學生按成績排序?
0到100之間一共有101(M)個成績值,給每個成績值建立一個桶,桶實際上是鏈表;每輸入的一個同學的成績,就按成績將該同學放入相對于的鏈表中
void Bucket_Sort ( ElementType A[ ] , int N )
{
count[ ] 初始化 ;
while ( 讀入1個學生成績grade )
將該學生插入count[ garde ]鏈表;
for ( i = 0 ; i < M ; i ++ ) { // M是成績值總數
if ( count[ i ] )
輸出整個count [ i ]鏈表;
}
}
基數排序 eg:假設有N=10個整數,每個整數的值在0到999之間(于是有M=1000個不同的值)此時再用桶排序則不合算
基數是進制的基數,如0到999之間的數,最多是三位數,每個位上的基數有10種可能
輸入序列:64,8,216,512,27,729,0,1,343,125
用“次位優先”(Least Significant Digit)即優先比較個位
建立0~9十個桶,第一趟排序按照每個數字個位上的數放入相應的桶,第二趟按十位上的數對應放入相應的桶,第三趟比百位數,
時間復雜度 T=O(P(N+B))
如果桶的數量足夠小,整個復雜度是線性的
多關鍵字排序,如撲克牌按花色、面值兩種關鍵字排序
多關鍵字可理解為多位數字,花色是十位,面值是個位
若用主位優先,分為四個桶,再在每個桶里按面值排序
次位排序更為便捷,為面值建立13個桶;將結果合并,然后再為花色建四個桶
-排序算法的比較:
簡單排序:簡單選擇排序、冒泡排序、直接插入排序。
選擇排序是跳著交換的,導致其不穩定性;簡單排序時間復雜度較高,不過代碼較簡單,其中冒泡排序和直接插入排序是穩定的
希爾排序平均時間復雜度較低,但它的好壞取決于增量序列,不過最壞情況下時間復雜度依然是N的平方
堆排序和歸并排序,理論上,時間復雜度都是 N*logN,其中堆排序的N可能很大,所以某些情況下會比快速排序要慢,且它是不穩定的;歸并排序是穩定的,但需要一定空間;
快速排序是遞歸的,所以需要logN的空間復雜度,最壞情況下時間復雜度是N的平方,是不穩定排序
基數排序某些情況下近乎線性,取決于桶