【算法设计与分析】排序

排序

  • 冒泡
  • 插入
  • 选择
  • 快排
  • 归并排序
  • 堆排序
  • 计数排序(Comparison-Counting Sort)
  • 桶排序

排序的分类

按是否占用额外内存(原地排序):

out-place 归并排序、计数排序、基数排序、桶排序
in-place 冒泡排序、插入排序、选择排序、快排、堆排序

按是否为稳定排序分类:

stable 冒泡排序、插入排序、归并排序、计数排序、基数排序、桶排序
unstable 选择排序(5 8 5 2 9)、快排、堆排序

PS:如何修改不稳定的排序算法使之稳定?
对每个输入的元素加入一个索引index,排序完之后相同的元素按照index再次排序即可。

按时间复杂度:

算法 时间复杂度 是否基于比较
冒泡、插入、选择 O(n^2)
快排、归并 O(nlogn)
计数、基数、桶 O(n)

总结:

【算法设计与分析】排序_第1张图片



冒泡排序

特点 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;  //右-左+1
	int *tmp = new int[len]; //开辟len个int型的变量
	int k = 0;
	int i = l;
	int j = mid + 1;    //这里一定要从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位
分布值更新为:

1 4 5

排序数组:

坐标 0 1 2 3 4 5
13

第2个出现的数字是11,排在第0位
分布值更新为:

0 4 5

排序数组:

坐标 0 1 2 3 4 5
11 13

第3个出现的数字是12,排在第3位
分布值更新为:

0 3 5

排序数组:

坐标 0 1 2 3 4 5
11 12 13

第4个出现的数字是13,排在第4位
分布值更新为:

0 4 4

排序数组:

坐标 0 1 2 3 4 5
11 12 13 13

第5个出现的数字是12,排在第2位
分布值更新为:

0 2 5

排序数组:

坐标 0 1 2 3 4 5
11 12 12 13 13

最后一个出现的数字是12,排在第1位
分布值更新为:

0 1 5

排序数组:

坐标 0 1 2 3 4 5
11 12 12 12 13 13



桶排序
性质 stable sort、out-place sort
最优/最差时间复杂度

你可能感兴趣的:(算法设计与分析)