1.概述
大一学生第一篇博客,记录下自己看网课的心得,文中有错误或是不足的地方还请指出。
2.算法简介
快速排序(英语:Quicksort),又称划分交换排序,简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序 n个项目要O(nlog n)(大O符号)次比较。在最坏状况下则需要 O(n2)次比较,但这种状况并不常见。事实上,快速排序 O(nlog n)通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。
3.算法实现
快速排序采用了一种分而治之的思想, 将数组分为三部分:pivot,比pivot小的数和比pivot大的数,其中pivot是从数组A中选的一个主元。算法的伪代码如下:
void quick_sort(int *a, int n)
{
if ( N < 2 )
return;
pivot = 从a中选一个主元;
将 S = {
a \ pivot } 分成2个独立子集:
a1 = {
b | b <= pivot } 和
a2 = {
b | b >= pivot };
a = quick_sort(a1,n1)
{
pivot}
quick_sort(a2,n2);
}
可以看到,整个算法的排序步骤就是划分子集后进行递归排序。
3.1.选主元
· 如果令 pivot = a[0]
在传统的快速排序中,往往选择头元素作为pivot,但是这样在数组基本有序的情况会出现很多次没有用处的比较。下面是一个已经有序的数组:我们可以看到的是
算法首先将1和后面的元素分成两部分:
1 2 3 4 5 6 ··· N-1 N
&再将2和后面的元素分成两部分:
2 3 4 5 6 ··· N-1 N
再将3和后面的元素分成两部分:
3 4 5 6 ··· N-1 N
······
上述的过程用时间复杂度表示如下:
T ( N ) = O( N ) + T ( N–1 )
= O( N ) + O ( N–1 ) + T( N–2 )
= O( N ) + O ( N–1 ) + ··· + O( 1 )
= O( N2 )
· 若使用rand()函数随机取pivot
采用此种方法会直接调库,相比于在数组中选择一个数字,调库更容易返回一个大于或小于数组中所有元素的值。这样的话,时间开销太大,十分浪费内存,显然不是最优解。
· 分别取头尾和中间的数,取其中位数作为pivot的值
例如8、12、3的中位数就是8,就使用8作为pivot,下面我们来看看代码
int median3(int *a, int left, int right) {
int center = left + (right - left) / 2; // Use this form to prevent overflow
if (a[left] > a[center]) {
swap(a, left, center);
}
if (a[left] > a[right]) {
swap(a, left, right);
}
if (a[center] > a[right]) {
swap(a, center, right);
}
// In this time, a[left] <= a[center] <= a[right]
// Hide the pivot on the position of right-1
swap(a, center, right - 1);
return a[right - 1];
}
在第3行代码中,采用了防止溢出的形式;
第4~7行代码,将最小值交换到了 a[left];
第8~9行代码,确定了剩下两个元素的次序。
这时我们将