| 方法 | 
最好 | 
最坏 | 
平均 | 
空间复杂度 | 
稳定性 | 
| 冒泡排序 | 
$O(n)$ | 
$O(n^2)$ | 
$O(n^2)$ | 
$O(1)$ | 
稳定 | 
| 快速排序 | 
$O(n\log_{2}n)$ | 
$O(n^2)$ | 
$O(n\log_{2}n)$ | 
$O(n\log_{2}n)$ | 
不稳定 | 
| 直接插入排序 | 
$O(n)$ | 
$O(n^2)$ | 
$O(n^2)$ | 
$O(1)$ | 
稳定 | 
| 希尔排序 | 
 | 
 | 
$O(n^{1.3})$ | 
$O(1)$ | 
不稳定 | 
| 简单选择排序 | 
$O(n^2)$ | 
$O(n^2)$ | 
$O(n^2)$ | 
$O(1)$ | 
不稳定 | 
| 堆排序 | 
$O(n\log_{2}n)$ | 
$O(n\log_{2}n)$ | 
$O(n\log_{2}n)$ | 
$O(1)$ | 
不稳定 | 
| 归并排序 | 
$O(n\log_{2}n)$ | 
$O(n\log_2{2}n)$ | 
$O(n\log_{2}n)$ | 
$O(n)$ | 
稳定 | 
| 基数排序 | 
$O(d(r+n))$ | 
$O(d(r+n))$ | 
$O(d(r+n))$ | 
$O(r)$ | 
稳定 | 
排序算法的稳定性: 若在原始序列中,$a_i$和$a_j$的关键字相同,$a_i$出现在$a_j$前,经过某种方法排序后,$a_i$的位置仍然在$a_j$前,则称这种算法是稳定的。
交换排序
基本排序
1  | void sort(int arr[], int n)  | 
2  | { | 
3  | 	int i,j;  | 
4  | 	for(i = 0; i < n-1; i++)  | 
5  | 	{ | 
6  | 		for(j = i + 1; j < n; j++)  | 
7  | 		{ | 
8  | 			if(arr[i] < arr[j])  | 
9  | 			{ | 
10  | 				swap(&arr[i], &arr[j]);  | 
11  | 			}  | 
12  | 		}  | 
13  | 	}  | 
14  | }  | 
 
冒泡排序
1  | void bubbleSort(int arr[], int n){ | 
2  | 	int i, j;  | 
3  | 	for(i = 0; i < n; i++){ | 
4  | 		for(j = 0; j < n - i - 1; j++){ | 
5  | 			if(arr[j] < arr[j+1])  | 
6  | 				swap(&arr[j], &arr[j+1]);  | 
7  | 		}  | 
8  | 	}  | 
9  | }  | 
 
改进的冒泡排序
在冒泡排序中除了将最小的数据移动到靠后的位置外,还使较小的数据逐渐靠近合适的位置。
1  | void bubbleSort(int arr[], int n)  | 
2  | { | 
3  |   int i, j;  | 
4  |   int flag = 1;  | 
5  |   for(i = 0; i < n && flag; i++)  | 
6  |   { | 
7  |     flag = 0;  | 
8  |     for(j = 0; j < n - i - 1; i++)  | 
9  |     { | 
10  |       if(arr[j] < arr[j+1])  | 
11  |       { | 
12  |         flag = 1;  | 
13  |         swap(&arr[j], &arr[j+1]);  | 
14  |       }  | 
15  |     }  | 
16  |   }  | 
17  | }  | 
 
快速排序
快速排序的基本思想是: 通过一遍排序,,将序列中的数据分割为两部分,其中一部分的所有数据都比另一部分小;然后按照此种方法,对两部分数据分别进行快速排序,直到参与排序的两部分都有序为止。
我们需要一个关键字key来作为分割标准,将数据与key进行比较来把数据分割为两部分。
如下所示,对原始序列5, 0, 9, 8, 1, 4, 6, 3, 7, 5按关键字key = arr[0] = 5进行一次快速排序的过程:
| 开始 | 
a[0] | 
a[1] | 
a[2] | 
a[3] | 
a[4] | 
a[5] | 
a[6] | 
a[7] | 
a[8] | 
a[9] | 
| 开始状态 | 
5(i,key) | 
0 | 
9 | 
8 | 
1 | 
4 | 
6 | 
3 | 
7 | 
5(j) | 
| a[i] = a[j] | 
3(i) | 
0 | 
9 | 
8 | 
1 | 
4 | 
6 | 
3(j) | 
7 | 
5 | 
| a[j] = a[i] | 
3 | 
0 | 
9(i) | 
8 | 
1 | 
4 | 
6 | 
9(j) | 
7 | 
5 | 
| a[i] = a[j] | 
3 | 
0 | 
4(i) | 
8 | 
1 | 
4(j) | 
6 | 
9 | 
7 | 
5 | 
| a[j] = a[i] | 
3 | 
0 | 
4 | 
8(i) | 
1 | 
8(j) | 
6 | 
9 | 
7 | 
5 | 
| a[i] = a[j] | 
3 | 
0 | 
4 | 
1(i) | 
1(j) | 
8 | 
6 | 
9 | 
7 | 
5 | 
| a[j] = a[i] | 
3 | 
0 | 
4 | 
1 | 
1(i,j) | 
8 | 
6 | 
9 | 
7 | 
5 | 
| a[i] = key | 
3 | 
0 | 
4 | 
1 | 
5(i,j) | 
8 | 
6 | 
9 | 
7 | 
5 | 
接着对上述结果的两个子序列重复进行上述快速排序过程:
| a[0] | 
a[1] | 
a[2] | 
a[3] | 
| 3 | 
0 | 
4 | 
1 | 
| a[0] | 
a[1] | 
a[2] | 
a[3] | 
a[4] | 
| 8 | 
6 | 
9 | 
7 | 
5 | 
1  | void QuickSort(int arr[], int left, int right)  | 
2  | { | 
3  |   if(left >= right)    | 
4  |   { | 
5  |      return;  | 
6  |   }  | 
7  | 
  | 
8  |   int i = left;  | 
9  |   int j = right;  | 
10  |   int key = arr[i];    | 
11  | 
  | 
12  |   while(i < j)   | 
13  |   { | 
14  |     while((i < j) && (key <= arr[j]))  | 
15  |     { | 
16  |       j--;   | 
17  |     }  | 
18  | 
  | 
19  |     arr[i] = arr[j];   | 
20  | 
  | 
21  |     while((i < j) && (key >= arr[i]))  | 
22  |     { | 
23  |       i++;   | 
24  |     }  | 
25  | 
  | 
26  |     arr[j] = arr[i];   | 
27  | 
  | 
28  |       | 
29  |   }  | 
30  | 
  | 
31  |   arr[i] = key;    | 
32  | 
  | 
33  |     | 
34  |   QuickSort(arr, left, i - 1);  | 
35  |   QuickSort(arr, i + 1, right);  | 
36  | }  | 
 
插入排序
插入排序的基本思想是: 将一条记录插入到一组已经有序的序列中,继而得到一个有序的,数据个数加1的新序列。
直接插入排序
直接插入排序将序列开始的第一个数据视为有序序列,另一部分视为待排序序列。
1  | void InsertSort(int arr[], int n)  | 
2  | { | 
3  |   int i,j;  | 
4  |   for(i = 1 ; i < n; i++)  | 
5  |   { | 
6  |     int tmp = arr[i];  | 
7  |     if(tmp > arr[i - 1])  | 
8  |     { | 
9  |         | 
10  |       for(j = i - 1; j >= 0 && tmp > arr[j]; j--)  | 
11  |       { | 
12  |         arr[j+1] = arr[j];  | 
13  |       }  | 
14  |       arr[j+1] = tmp;  | 
15  |     }  | 
16  |   }  | 
17  | }  | 
 
折半插入排序
在直接插入排序中,主要的时间消耗在数据的比较和移动上,由于前半部分的数据已经有序,则可以采取折半查找的方法来提高查找速度。
折半插入排序节省了排序过程中的比较次数,但是移动次数与直接插入排序相同,所以时间复杂度还是$O(n^2)$。
1  | int BinarySort(int arr[],int n)  | 
2  | { | 
3  |   int i, j;  | 
4  |   int low, hight, m;  | 
5  |   int tmp;  | 
6  |   for(i = 1; i < n; i++)  | 
7  |   { | 
8  |     tmp = arr[i];  | 
9  |     low = 0;  | 
10  |     high = i - 1;  | 
11  |     while(low <= hight)  | 
12  |     { | 
13  |       m = (low + high) / 2;  | 
14  |       if(tmp > arr[m])  | 
15  |         high = m - 1;  | 
16  |       else  | 
17  |         low = m + 1;  | 
18  |     }  | 
19  | 
  | 
20  |     for(j = i - 1; j >= high + 1; j--)  | 
21  |       arr[j+1] = arr[j];  | 
22  |     arr[high+1] = tmp;  | 
23  |   }  | 
24  | }  | 
 
希尔排序
无论是直接插入排序还是折半插入排序,都是将待排序序列视为一个分组,而希尔排序将原始序列按照下标的一定增量分为多个序列,在每个序列中执行直接插入排序。
希尔排序即解决了比较次数的问题,也解决了移动位数的问题。
1  | void ShellSort(int arr[], int n)  | 
2  | { | 
3  |   int i, j, d;  | 
4  |   int tmp;  | 
5  |   d = n / 2;  | 
6  |     | 
7  |   while(d > 0)  | 
8  |   { | 
9  |     for(i = d; i < n; i++)  | 
10  |     { | 
11  |       tmp = arr[i];  | 
12  |       j= i - d;  | 
13  |       while(j >= 0 && tmp > arr[j])  | 
14  |       { | 
15  |         arr[j+d] = arr[j];  | 
16  |         j = j - d;  | 
17  |       }  | 
18  |       arr[j+d] = tmp;  | 
19  |     }  | 
20  |     d = d / 2;  | 
21  |   }  | 
22  | }  | 
 
选择排序
选择排序是从待排序的序列中选择出最大(最小)值,交换该元素与待排序序列头元素,直到所有待排序的数据有序为止
简单选择排序
1  | void SelectSort(int arr[], int n)  | 
2  | { | 
3  |   int i,j,max;  | 
4  |   for(i = 0; i < n; i++)  | 
5  |   { | 
6  |     max = i;  | 
7  |     for(j = i; j < n; j++)  | 
8  |     { | 
9  |       if(arr[max] < arr[j])  | 
10  |         max = j;  | 
11  |     }  | 
12  |     if(i != max)  | 
13  |       swap(&arr[i], &arr[max]);  | 
14  |   }  | 
15  | }  | 
 
堆排序
二叉堆中所有非终端结点的值均不小于(大于)其左右孩子的值。
1  | void HeapAdjust(int arr[], int i, int n)  | 
2  | { | 
3  |   int nChild;  | 
4  |   int tmp;  | 
5  |   for(; 2 * i + 1 < n; i = nChild)  | 
6  |   { | 
7  |     nChild = 2 * i + 1;  | 
8  |     if(nChild < n - 1; && arr[nChild+1] > arr[nChild])  | 
9  |       ++nChild;  | 
10  |     if(arr[i] < arr[nChild])  | 
11  |     { | 
12  |       tmp = arr[i];  | 
13  |       arr[i] = arr[nChild];  | 
14  |       arr[nChild] = tmp;  | 
15  |     }  | 
16  |     else  | 
17  |       break;  | 
18  |   }  | 
19  | }  | 
 
1  | void HeapSort(int arr[], int n)  | 
2  | { | 
3  |   int i;  | 
4  |     | 
5  |   for(i = (n - 1) / 2; i >= 0; i--)  | 
6  |     HeapAdjust(arr, i, n);  | 
7  | 
  | 
8  |   for(i = n -1; i > 0; i--)  | 
9  |   { | 
10  |     arr[i] = arr[0] ^ arr[i];  | 
11  |     arr[0] = arr[0] ^ arr[i];  | 
12  |     arr[i] = arr[0] ^ arr[i];  | 
13  |       | 
14  |     HeapAdjust(arr, 0, i);  | 
15  | 	}  | 
16  | }  | 
 
归并排序
归并排序就是将两个序列合并在一起,并使其有序。
1  | void Merge(int arr[], int tmp[], int start, int mid, int  end)  | 
2  | { | 
3  |   int i = start, j = mid+1; k = start;  | 
4  |     | 
5  |   while(i != mid+1 && j != end+1)  | 
6  |   { | 
7  |     if(arr[i] >= arr[j])  | 
8  |       tmp[k++] = arr[j++];  | 
9  |     else  | 
10  |       tmp[k++] = arr[i++];  | 
11  |   }  | 
12  | 
  | 
13  |   while(i != mid+1)  | 
14  |     tmp[k++] = arr[i++];  | 
15  |   while(j != end+1)  | 
16  |     tmp[k++] = arr[j++];  | 
17  | 
  | 
18  |   for(i = start; i <= end; i++)  | 
19  |     arr[i] = tmp[i];  | 
20  | }  | 
 
1  | void MergeSort(int arr[], int tmp, int start, int end)  | 
2  | { | 
3  |   int mid;  | 
4  |   if(start < end)  | 
5  |   { | 
6  |     mid = (start + end) / 2;  | 
7  |     MergeSort(arr, tmp, start, mid);  | 
8  |     MergeSort(arr, tmp, mid+1, end);  | 
9  |     Merge(arr, tmp, start, midi, end);  | 
10  |   }  | 
11  | }  | 
 
基数排序
计数排序,桶排序,多关键字排序
1  | #define MAXD 8  | 
2  | typedef struct node  | 
3  | { | 
4  |   char key[MAXD];   | 
5  |   struct node* next;  | 
6  | }RecType;  | 
7  | 
  | 
8  | void RadixSort(RecType* &p, int r, int d)  | 
9  | { | 
10  |   RecType * head[MAXR], *tail[MAXR], *t;  | 
11  |   int i, j, k;  | 
12  |   for(i = d - 1; i >= 0; i--)  | 
13  |   { | 
14  |     for(j = 0; j < r; j++)  | 
15  |       head[j]=tail[j]=NULL;  | 
16  |      | 
17  |     while(p != NULL)  | 
18  |     { | 
19  |       k = p->key[i] - '0';  | 
20  |       if(head[k]==NULL)  | 
21  |       { | 
22  |         head[k] = p;  | 
23  |         tail[k] = p;  | 
24  |       }  | 
25  |       else  | 
26  |       { | 
27  |         tail[k]->next = p;  | 
28  |         tail[k] = p;  | 
29  |       }  | 
30  |       p = p->next;  | 
31  |     }  | 
32  |     p = NULL;  | 
33  |      | 
34  |     for(j = 0; j < r; j++)  | 
35  |      if(head[j] != NULL)   | 
36  |      { | 
37  |        if(p == NULL)  | 
38  |        { | 
39  |          p = head[j];  | 
40  |          t = tail[j];  | 
41  |        }  | 
42  |        else  | 
43  |        { | 
44  |          t->next = head[j];  | 
45  |          t = tail[j];  | 
46  |        }  | 
47  |      }  | 
48  |      t->next = NULL;  | 
49  |   }  | 
50  | }  |