排序
- 冒泡
- 插入
- 选择
- 快排
- 归并排序
- 堆排序
- 计数排序(Comparison-Counting Sort)
- 桶排序
排序的分类
按是否占用额外内存(原地排序):
out-place |
归并排序、计数排序、基数排序、桶排序 |
in-place |
冒泡排序、插入排序、选择排序、快排、堆排序 |
按是否为稳定排序分类:
stable |
冒泡排序、插入排序、归并排序、计数排序、基数排序、桶排序 |
unstable |
选择排序(5 8 5 2 9)、快排、堆排序 |
PS:如何修改不稳定的排序算法使之稳定?
对每个输入的元素加入一个索引index,排序完之后相同的元素按照index再次排序即可。
按时间复杂度:
算法 |
时间复杂度 |
是否基于比较 |
冒泡、插入、选择 |
O(n^2) |
是 |
快排、归并 |
O(nlogn) |
是 |
计数、基数、桶 |
O(n) |
否 |
总结:
冒泡排序
特点 |
stable sort、in-place sort |
最优复杂度 |
数组已经排好序时:O(n) |
最差时间复杂度 |
数组逆序排序时:O(n^2) |
平均时间复杂度 |
O(n^2) |
插入排序
特点 |
stable sort、in-place sort |
最优复杂度 |
数组已经排好序时:O(n) |
最差时间复杂度 |
数组逆序排序时:O(n^2) |
适合数组类型 |
比较适合“少量元素数组” 插入排序的排序速度是数组中逆序对的个数,逆序越少,插入排序越快 |
插入排序的过程:
数组的左边有序,右边无序,依次从右边选取数字插入到左边合适的位置。
排序初始时,数组左边为空
Q1.快排是否一定比插入排序快?(数据不随机)
不是的,当数组是顺序排好时,使用快排的时间复杂度是O(n^2),而插入排序的时间复杂度是O(n)
Q2.冒泡排序和插入排序哪个快?
插入排序快。插入排序的速度是逆序对的个数,而冒泡排序中,执行“交换”过程的次数就是逆序对的个数,因此冒泡运行的时间至少是逆序对的个数。
选择排序
特点 |
unstable sort、in-place sort |
最优/最差复杂度 |
O(n^2) |
快速排序
性质 |
in-place sort |
最优时间复杂度 |
O(nlogn) |
最差时间复杂度 |
数组已经排序时:O(n^2) |
特点 |
分治的思想 |
代码:
void QuickSort(int a[], int l, int r)
{
if (l >= r) return;
int i = l, j = r, key = a[i];
while (i < j)
{
while (i < j && key <= a[j])
j--;
a[i] = a[j];
while (i < j && key >= a[i])
i++;
a[j] = a[i];
}
a[i] = key;
QuickSort(a, l, i - 1);
QuickSort(a, i + 1, r);
}
归并排序
性质 |
out-place sort |
最优/最差时间复杂度 |
O(nlogn) |
特点 |
分治的思想解决问题 |
代码:
void merge(int a[], int l,int r,int mid)
{
int len = r - l + 1;
int *tmp = new int[len];
int k = 0;
int i = l;
int j = mid + 1;
while (i <= mid && j <= r)
tmp[k++] = a[i] < a[j] ? a[i++] : a[j++];
while (i <= mid)
tmp[k++] = a[i++];
while (j <= r)
tmp[k++] = a[j++];
for (k = 0; k < len; k++)
a[l++] = tmp[k];
}
void merge_sort(int a[], int l, int r)
{
if (l == r) return;
int mid = (l + r) / 2;
merge_sort(a, l, mid);
merge_sort(a, mid + 1, r);
merge(a, l, r, mid);
}
Q1.归并排序的缺点是什么?
需要额外的空间(额外的多少空间)
Q2.归并排序和快速排序哪个更快?
快排更快,因为虽然渐近复杂度一样,但是归并排序的系数比快排的大
Q3.归并排序如何改进?
在数组长度为k时使用插入排序(插入排序适合对小数组排序)
其中k=O(logn),此时算法的复杂度为O(nlogn)
堆排序
性质 |
unstable sort、in-place sort |
最优/最差时间复杂度 |
O(nlogn) |
计数排序(comparison-counting sort)
性质 |
stable-sort,out-place sort |
最优/最差时间复杂度 |
O(n^2) |
计数排序的过程是,从第一个数开始,记录比它更小的数有多少,这个数字就是它排序完成的数组坐标。
计数排序的延伸还有一个分布计数排序(distribution counting) 。
考虑数组:
13 11 12 13 12 12
已知他们来自集合{11,12,13}。在这种情况下使用分布计数排序效果较好:
首先统计这些数字出现的频率,然后算出分布值:
数组值 |
11 |
12 |
13 |
频率 |
1 |
3 |
2 |
分布值 |
1 |
4 |
6 |
排序过程为:
第一个出现的数字是13,排在第5位
分布值更新为:
排序数组:
第2个出现的数字是11,排在第0位
分布值更新为:
排序数组:
第3个出现的数字是12,排在第3位
分布值更新为:
排序数组:
第4个出现的数字是13,排在第4位
分布值更新为:
排序数组:
坐标 |
0 |
1 |
2 |
3 |
4 |
5 |
|
11 |
|
|
12 |
13 |
13 |
第5个出现的数字是12,排在第2位
分布值更新为:
排序数组:
坐标 |
0 |
1 |
2 |
3 |
4 |
5 |
|
11 |
|
12 |
12 |
13 |
13 |
最后一个出现的数字是12,排在第1位
分布值更新为:
排序数组:
坐标 |
0 |
1 |
2 |
3 |
4 |
5 |
|
11 |
12 |
12 |
12 |
13 |
13 |
桶排序
性质 |
stable sort、out-place sort |
最优/最差时间复杂度 |
|