方法 |
最好 |
最坏 |
平均 |
空间复杂度 |
稳定性 |
冒泡排序 |
$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 | } |