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