快速排序
快速排序,简称快排,在排序中,快速排序其实就是我们前面认为最慢的冒泡排序的升级,它们都属于交换排序类。即快排也是通过不断比较和移动交换来实现排序的,不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。
基本思想:
任取待排元素序列中的某元素中的作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于
基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止
void QuickSort(Datatype* a, int left, int right)
{
assert(a);
if (left >= right)
return;
int div = Partition(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
来看下面几种按照基准值将区间划分的方法
1.左右指针法:
//快速排序左右指针法
int Partition1(Datatype* a, int left, int right)
{
Datatype key = a[right];
int begin = left, end = right;
while (begin < end)
{
if (a[begin] <= key)
{
begin++;
continue;
}
/*while (begin < end&&a[begin] <= key)
begin++;*/
if (a[end] >= key)
{
end--;
continue;
}
/*while (begin < end&& a[end] >= key)
end--;*/
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[right]);
return begin;
}
2.挖坑法
//挖坑法
int Partition2(Datatype* a, int left, int right)
{
Datatype key = a[right];
int tig = right;
int begin = left, end = right;
while (begin < end)
{
if (a[begin] <= key)
{
begin++;
continue;
}
a[tig] = a[begin];
tig = begin;
if (a[end] >= key)
{
end--;
continue;
}
a[tig] = a[end];
tig = end;
}
a[tig] = key;
return tig;
}
3.神奇的联动(prev)指针
//神奇的prev指针
int Partition3(Datatype* a, int left, int right)
{
Datatype key = a[right];
int prev = left - 1;
int cur = left;
while (cur < right)
{
/*if (a[cur] < key&& ++prev!=cur)
{ //代码短,难理解,以下代码代替
Swap(&a[prev], &a[cur]);
}*/
if (a[cur] < key)
{
prev++;
if (prev!=cur)
Swap(&a[prev], &a[cur]);
}
cur++;
}
Swap(&a[++prev], &a[right]);
return prev;
}
快速排序算法虽然整体上比其他算法更快,但当数据基本趋于有序的时候,每次基准值都是最后一位,区间没有被划分开。所以算法最差的时间复杂度为O(N2).
常见的优化方案有:
1.随机数法
2.三数取中法
3.小区间优化法
//1.随机数法
int Partition(Datatype* a, int left, int right)
{
int index = RandomInRange(start,end);
Swap(&a[index],&a[right]);
Datatype key = a[right];
int begin = left, end = right;
while (begin < end)
{
while (begin < end&&a[begin] <= key)
begin++;
while (begin < end&& a[end] >= key)
end--;
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[right]);
return begin;
}
//2.三数取中法:
int MidInRange(Datatype* a, int left, int right)
{
int mid = left + ((right - left) >> 1);
if (a[left] < a[right])
{
if (a[mid] < a[left])
return left;
else if (a[mid] < a[right])
return mid;
else
return right;
}
if (a[right] > a[left])
{
if (a[mid] > a[right])
return right;
else if (a[mid] > a[left])
return mid;
else
return left;
}
}
int Partition(Datatype* a, int left, int right)
{
int index = MidInRange(a,start,end);
Swap(&a[index],&a[right]);
Datatype key = a[right];
int begin = left, end = right;
while (begin < end)
{
while (begin < end&&a[begin] <= key)
begin++;
while (begin < end&& a[end] >= key)
end--;
Swap(&a[begin], &a[end]);
}
Swap(&a[begin], &a[right]);
return begin;
}
//3.小区间优化法,当区间较小时可以采用插入排序,减少函数递归时调用栈帧的时间开销
void QuickSort(Datatype* a, int left, int right)
{
assert(a);
if (left >= right)
return;
if (right - left <= 10)
InsertSort(a, right-left+1);
int div = Partition(a, left, right);
QuickSort(a, 0, div - 1);
QuickSort(a, div + 1, right);
}