快速排序
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:在待排序元素序列中任取一个元素key,按照key将该序列分割成两子序列,左子序列中所有元素均小于key,右子序列中所有元素均大于key,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
代码1(只在最好的情况下效率高)
int PartSort(int* a, int begin, int end)
{
int key = begin;
while (begin < end)
{
//end找小
while (begin < end && a[end] >= a[key])
--end;
//begin找大
while (begin < end && a[begin] <= a[key])
++begin;
Swap(&a[begin], &a[end]);
}
Swap(&a[key], &a[end]);
return end;
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
int div = PartSort(a, left, right);
QuickSort(a, left, div - 1);
QuickSort(a, div + 1, right);
}
以上代码,如果每次选的数恰好都是中间数,快排最优;如果序列有序,每次选的数是最大或者最小,快排最坏。
快速排序优化
- 三数取中法选key
int GetMidIndex(int* a, int begin, int end)
{
int mid = begin + ((end - begin) >> 1);
if (a[begin] < a[mid])
{
if (a[mid] < a[end])
return mid;
else if (a[begin] > a[end])
return begin;
else
return end;
}
else //a[begin] > a[mid]
{
if (a[mid] > a[end])
return mid;
else if (a[begin] < a[end])
return begin;
else
return end;
}
}
- 递归到小的子区间时,可以考虑使用插入排序
void QuickSort2(int* a, int left, int right)
{
if (left >= right)
return;
//[left, div-1] [div+1, right]
if (right - left > 10)
{
int div = PartSort3(a, left, right);
QuickSort2(a, left, div - 1);
QuickSort2(a, div + 1, right);
}
else
{
InsertSort(a + left, right - left + 1);
}
}
划分区间的常见方式有:
- hoare版本
int PartSort1(int* a, int begin, int end)
{
//三数取中法选key
int mid = GetMidIndex(a, begin, end);
Swap(&a[begin], &a[mid]);
int key = begin;
while (begin < end)
{
//end找小
while (begin < end && a[end] >= a[key])
--end;
//begin找大
while (begin < end && a[begin] <= a[key])
++begin;
Swap(&a[begin], &a[end]);
}
Swap(&a[key], &a[end]);
return end;
}
- 挖坑法
int PartSort2(int*a, int begin, int end)
{
int tmp = a[begin];
while (begin < end)
{
//找小,填到左边的坑
while (begin < end && a[end] >= tmp) //必须有等于,否则可能会陷入死循环
--end;
a[begin] = a[end];
//找大,填到右边的坑
while (begin < end && a[begin] <= tmp)
++begin;
a[end] = a[begin];
}
a[begin] = tmp;
}
- 前后指针版本
int PartSort3(int*a, int begin, int end)
{
int key = begin;
int prev = begin;
int cur = begin + 1;
while (cur <= end)
{
if (a[cur] < a[key] && ++prev != cur)
{
Swap(&a[prev], &a[cur]);
}
++cur;
}
Swap(&a[key], &a[prev]);
return prev;
}
快速排序的特性总结:
- 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
- 时间复杂度:O(N*logN)
- 空间复杂度:O(logN)
- 稳定性:不稳定