1.快速排序
不穩定
分而治之
找主元pivot,小於主元划分為一個子集,大於主元的划分為一個子集
然后進行遞歸
最好情況:每次主元正好中分,T(N) = O( NlogN )
選主元 的方法有很多,這里用 取頭、中、尾的中位數。
直接選A[0]為pivot,時間復雜度T ( N ) = O( N ) + T ( N–1 ) = O( N ) + O ( N–1 ) + T( N–2 ) = = O( N ) + O ( N–1 ) + …+ O( 1 ) = O( N^2 )
隨機取pivot:rand()函數 也很費
取頭、中、尾的中位數……
子集划分
i指向8,j指向7.
8>6,i不動,7>6,j--;j指向2,2<6,j不動 交換8和2;
i++,i指向1,1<6,i++;i指向4,4<6,i++;i指向9,9>6,i不動;
j--,j指向5,5<6,j不動,交換5和9;
i++,i指向0,0<6,i++;i指向3,3<6,i++;i指向9,9>6,i不動;
j--,j指向3,3<6,j不動.
i>j,結束,6放到i位置,即6和9交換
如果有元素正好等於pivot怎么辦:
停下來交換:會產生很多不必要的交換。(一串相等的序列)
不理它,繼續移動指針:pivot位置靠近一段,最糟糕會在最右邊端,和pivot取A[0]一樣,O( N^2 )
綜合考慮,選第一種,停下來交換。
小規模數據的處理
快速排序的問題
用遞歸……
對小規模的數據(例如N不到100)可能還不如插入排序快
解決方案
當遞歸的數據規模充分小,則停止遞歸,直接調用簡單排序(例如插入排序)
在程序中定義一個Cutoff的閾值

1 #include <stdio.h> 2 typedef int ElementType; 3 4 void Swap( ElementType *a, ElementType *b ) 5 { 6 ElementType t = *a; 7 *a = *b; 8 *b = t; 9 } 10 11 void InsertionSort(ElementType A[], int N) 12 { 13 int i; 14 for (int P = 1; P < N; P++ ) { 15 ElementType temp = A[P]; //取出未排序序列中第一個元素 16 for (i = P; i > 0 && A[i-1] > temp; i-- ) 17 A[i] = A[i-1]; //依次與已排序序列中元素比較並右移 18 A[i] = temp; 19 } 20 } 21 22 /* 快速排序 */ 23 ElementType Median3( ElementType A[], int Left, int Right ) 24 { 25 int Center = (Left+Right) / 2; 26 if ( A[Left] > A[Center] ) 27 Swap( &A[Left], &A[Center] ); 28 if ( A[Left] > A[Right] ) 29 Swap( &A[Left], &A[Right] ); 30 if ( A[Center] > A[Right] ) 31 Swap( &A[Center], &A[Right] ); 32 /* 此時A[Left] <= A[Center] <= A[Right] */ 33 Swap( &A[Center], &A[Right-1] ); /* 將基准Pivot藏到右邊*/ 34 /* 只需要考慮A[Left+1] … A[Right-2] */ 35 return A[Right-1]; /* 返回基准Pivot */ 36 } 37 38 void Qsort( ElementType A[], int Left, int Right ) 39 { /* 核心遞歸函數 */ 40 int Pivot, Cutoff = 50, Low, High;//閾值的定義 41 42 if ( Cutoff <= Right-Left ) { /* 如果序列元素充分多,進入快排 */ 43 Pivot = Median3( A, Left, Right ); /* 選基准 */ 44 Low = Left; High = Right-1; 45 while (1) { /*將序列中比基准小的移到基准左邊,大的移到右邊*/ 46 while ( A[++Low] < Pivot ) ; 47 while ( A[--High] > Pivot ) ; 48 if ( Low < High ) Swap( &A[Low], &A[High] ); 49 else break; 50 } 51 Swap( &A[Low], &A[Right-1] ); /* 將基准換到正確的位置 */ 52 Qsort( A, Left, Low-1 ); /* 遞歸解決左邊 */ 53 Qsort( A, Low+1, Right ); /* 遞歸解決右邊 */ 54 } 55 else InsertionSort( A+Left, Right-Left+1 ); /* 元素太少,用簡單排序 */ 56 } 57 58 void QuickSort( ElementType A[], int N ) 59 { /* 統一接口 */ 60 Qsort( A, 0, N-1 ); 61 } 62 63 int main() 64 { 65 int a[] = {34,8,64,51,32,21}; 66 QuickSort(a, 6); 67 for(int i = 0; i < 6; i++) 68 printf("%d ",a[i]); 69 return 0; 70 } 71 72 QuickSort
2.表排序
間接排序
定義一個指針數組(下標)作為“表”(table)
如果僅要求按順序輸出,則輸出:A[ table[0] ], A[ table[1] ], ……, A[ table[N-1] ]
物理排序
N個數字的排列由若干個獨立的環組成
用temp記錄初值 ,每次換位子修改table值,用if ( table[i] == i )判斷一個環的結束
復雜度
最好情況:初始即有序
最壞情況:
有[N / 2]向下取整 個環,每個環包含2個元素
需要[N / 2]向下取整 次元素移動
T = O( m N ) ,m 是每個A元素的復制時間。
3.基數排序
桶排序:假設我們有N 個學生,他們的成績是0到100之間的整數(於是有M = 101 個不同的成績值)。如何在線性時間內將學生按成績排序?
T(N, M) = O( M+N )
當M>>N時,桶排序不合算
次位優先LSB
假設我們有N = 10 個整數,每個整數的值在0到999之間(於是有M = 1000 個不同的值)。
輸入序列: 64, 8, 216, 512, 27, 729, 0, 1, 343, 125
用“次位優先”(Least Significant Digit) T=O(P(N+B))
多關鍵字的排序
一副撲克牌是按2種關鍵字排序的

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef int ElementType; 5 6 /* 基數排序 - 次位優先 */ 7 8 /* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */ 9 #define MaxDigit 4 10 #define Radix 10 11 12 /* 桶元素結點 */ 13 typedef struct Node *PtrToNode; 14 struct Node { 15 int key; 16 PtrToNode next; 17 }; 18 19 /* 桶頭結點 */ 20 struct HeadNode { 21 PtrToNode head, tail; 22 }; 23 typedef struct HeadNode Bucket[Radix]; 24 25 int GetDigit ( int X, int D ) 26 { /* 默認次位D=1, 主位D<=MaxDigit */ 27 int d, i; 28 29 for (i=1; i<=D; i++) { 30 d = X % Radix; 31 X /= Radix; 32 } 33 return d; 34 } 35 36 void LSDRadixSort( ElementType A[], int N ) 37 { /* 基數排序 - 次位優先 */ 38 int D, Di, i; 39 Bucket B; 40 PtrToNode tmp, p, List = NULL; 41 42 for (i=0; i<Radix; i++) /* 初始化每個桶為空鏈表 */ 43 B[i].head = B[i].tail = NULL; 44 for (i=0; i<N; i++) { /* 將原始序列逆序存入初始鏈表List */ 45 tmp = (PtrToNode)malloc(sizeof(struct Node)); 46 tmp->key = A[i]; 47 tmp->next = List; 48 List = tmp; 49 } 50 /* 下面開始排序 */ 51 for (D=1; D<=MaxDigit; D++) { /* 對數據的每一位循環處理 */ 52 /* 下面是分配的過程 */ 53 p = List; 54 while (p) { 55 Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */ 56 /* 從List中摘除 */ 57 tmp = p; p = p->next; 58 /* 插入B[Di]號桶尾 */ 59 tmp->next = NULL; 60 if (B[Di].head == NULL) 61 B[Di].head = B[Di].tail = tmp; 62 else { 63 B[Di].tail->next = tmp; 64 B[Di].tail = tmp; 65 } 66 } 67 /* 下面是收集的過程 */ 68 List = NULL; 69 for (Di=Radix-1; Di>=0; Di--) { /* 將每個桶的元素順序收集入List */ 70 if (B[Di].head) { /* 如果桶不為空 */ 71 /* 整桶插入List表頭 */ 72 B[Di].tail->next = List; 73 List = B[Di].head; 74 B[Di].head = B[Di].tail = NULL; /* 清空桶 */ 75 } 76 } 77 } 78 /* 將List倒入A[]並釋放空間 */ 79 for (i=0; i<N; i++) { 80 tmp = List; 81 List = List->next; 82 A[i] = tmp->key; 83 free(tmp); 84 } 85 } 86 87 int main() 88 { 89 int a[] = {34,8,64,51,32,21}; 90 LSDRadixSort(a, 6); 91 for(int i = 0; i < 6; i++) 92 printf("%d ",a[i]); 93 return 0; 94 }

1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef int ElementType; 5 6 /* 基數排序 - 主位優先 */ 7 8 /* 假設元素最多有MaxDigit個關鍵字,基數全是同樣的Radix */ 9 10 #define MaxDigit 4 11 #define Radix 10 12 13 /* 桶元素結點 */ 14 typedef struct Node *PtrToNode; 15 struct Node{ 16 int key; 17 PtrToNode next; 18 }; 19 20 /* 桶頭結點 */ 21 struct HeadNode { 22 PtrToNode head, tail; 23 }; 24 typedef struct HeadNode Bucket[Radix]; 25 26 int GetDigit ( int X, int D ) 27 { /* 默認次位D=1, 主位D<=MaxDigit */ 28 int d, i; 29 30 for (i=1; i<=D; i++) { 31 d = X%Radix; 32 X /= Radix; 33 } 34 return d; 35 } 36 37 void MSD( ElementType A[], int L, int R, int D ) 38 { /* 核心遞歸函數: 對A[L]...A[R]的第D位數進行排序 */ 39 int Di, i, j; 40 Bucket B; 41 PtrToNode tmp, p, List = NULL; 42 if (D==0) return; /* 遞歸終止條件 */ 43 44 for (i=0; i<Radix; i++) /* 初始化每個桶為空鏈表 */ 45 B[i].head = B[i].tail = NULL; 46 for (i=L; i<=R; i++) { /* 將原始序列逆序存入初始鏈表List */ 47 tmp = (PtrToNode)malloc(sizeof(struct Node)); 48 tmp->key = A[i]; 49 tmp->next = List; 50 List = tmp; 51 } 52 /* 下面是分配的過程 */ 53 p = List; 54 while (p) { 55 Di = GetDigit(p->key, D); /* 獲得當前元素的當前位數字 */ 56 /* 從List中摘除 */ 57 tmp = p; p = p->next; 58 /* 插入B[Di]號桶 */ 59 if (B[Di].head == NULL) B[Di].tail = tmp; 60 tmp->next = B[Di].head; 61 B[Di].head = tmp; 62 } 63 /* 下面是收集的過程 */ 64 i = j = L; /* i, j記錄當前要處理的A[]的左右端下標 */ 65 for (Di=0; Di<Radix; Di++) { /* 對於每個桶 */ 66 if (B[Di].head) { /* 將非空的桶整桶倒入A[], 遞歸排序 */ 67 p = B[Di].head; 68 while (p) { 69 tmp = p; 70 p = p->next; 71 A[j++] = tmp->key; 72 free(tmp); 73 } 74 /* 遞歸對該桶數據排序, 位數減1 */ 75 MSD(A, i, j-1, D-1); 76 i = j; /* 為下一個桶對應的A[]左端 */ 77 } 78 } 79 } 80 81 void MSDRadixSort( ElementType A[], int N ) 82 { /* 統一接口 */ 83 MSD(A, 0, N-1, MaxDigit); 84 } 85 86 int main() 87 { 88 int a[] = {34,8,64,51,32,21}; 89 MSDRadixSort(a, 6); 90 for(int i = 0; i < 6; i++) 91 printf("%d ",a[i]); 92 return 0; 93 }