快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。
基本思想:
任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
将区间按照基准值划分为左右两半部分的常见方式: Hoare法、挖坑法、前后指针法。
动图理解:
1.当选择最左做key时,右边先走。-->左右相遇时比key小。
2.当选择最右做key时,左边先走。-->左右相遇时比key大。
选择左边值为key,right先走,找比key小的值停下(跳过大的),left再走,找到比key大的值停下(跳过小的)。交换 left 与 right 的值,直到left,right相遇。相遇点的值和key值交换。
int Partion1(int* a, int left, int right)
{
// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);
int keyi = left;
while (left < right)
{
// 右边先走,找小,跳过大的
while (left < right && a[right] >= a[keyi])
--right;
//左边再走,找大,跳过小的
while (left < right && a[left] <= a[keyi])
++left;
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
return left;
}
注意:
快速排序优化:三数取中法选key:三数取中位数不会使最大或最小做key
动图理解:
1.当选择最左做key时,右边先走。
2.当选择最右做key时,左边先走。
右边找小,把比key小的值放进坑里,右边形成新的坑;左边找大扔到坑里,左边形成新的坑。
// 挖坑法
int Partion2(int* a, int left, int right)
{
// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);
int key = a[left];
//坑
int pivot = left;
while (left < right)
{
// 右边找小, 放到左边的坑里面
while (left < right && a[right] >= key)
{
--right;
}
a[pivot] = a[right];
pivot = right;
// 左边找大,放到右边的坑里面
while (left < right && a[left] <= key)
{
++left;
}
a[pivot] = a[left];
pivot = left;
}
a[pivot] = key;
return pivot;
}
注意:
先往坑里填值,再改变坑的位置。
选择方式:
cur找到比key小的就停下来,++prev,再交换prev和cur位置的值。
prev要么紧跟着cur,要么紧跟着比key大的序列。
// 推荐掌握这个 -- 思想三种大家都要掌握
int Partion3(int* a, int left, int right)
{
// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
int midi = GetMidIndex(a, left, right);
Swap(&a[midi], &a[left]);
int keyi = left;
int prev = left;
int cur = prev + 1;
while (cur <= right)
{
if (a[cur] < a[keyi] && ++prev != cur)
{
Swap(&a[cur], &a[prev]);
}
++cur;
}
Swap(&a[prev], &a[keyi]);
return prev;
}
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
// 小区间优化,当分割到小区间时,不再用递归分割思路让这段子区间有序
// 对于递归快排,减少递归次数
if (right - left + 1 < 10)
{
InsertSort(a + left, right - left + 1);
}
else
{
//类似二叉树遍历,根左右
int keyi = Partion(a, left, right);
// [left, keyi-1] keyi [keyi+1, right]
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
}
// 递归深度太深的程序,只能考虑改非递归
void QuickSortNonR(int* a, int left, int right)
{
ST st;
StackInit(&st);
StackPush(&st, left);
StackPush(&st, right);
while (!StackEmpty(&st))
{
int end = StackTop(&st);
StackPop(&st);
int begin = StackTop(&st);
StackPop(&st);
int keyi = Partion3(a, begin, end);
// [begin, keyi-1] keyi [keyi+1, end]
if (keyi + 1 < end)
{
StackPush(&st, keyi + 1);
StackPush(&st, end);
}
if (begin < keyi - 1)
{
StackPush(&st, begin);
StackPush(&st, keyi - 1);
}
}
StackDestroy(&st);
}