目录
1.基本思想
快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。(一般基准值取最左侧或左右侧元素)
2.主框架实现
void QuickSort(int array[],int left,int right)
{
if ( right-left <= 1)
return;
//按照基准值将对数组array的[left,right)区间元素进行划分
int div = Partion2(array, left, right);
//划分成功后,数组形成[left,div)和[div+1.right)两部分
//递归排[left,div)
QuickSort(array, left, div);
//递归排[div+1.right)
QuickSort(array, div + 1, right);
}
2.划分方式
将区间按照基准值划分为左右两半部分的常见方式有:
2.1hoare版本
重复以下步骤,直到划分成功:(当begin=end时循环结束)
a.先让begin从前往后找,找比基准值大的元素,找到之后停止
b.再让end从后往前找,找比基准值小的元素,找到之后停止
c.让后将begin位置上大于基准值的元素与end位置上小于基准值的元素进行交换
当循环结束后,将begin位置上的元素和区间最右侧的元素交换
最后返回begin或者end即可
具体实现:
//1.hoare版本
int Partion1(int array[], int left,int right)
{
int keyIndex = GetMiddleIndex(array, left, right);
Swap(&array[keyIndex], &array[right - 1]);
int begin = left;//begin不能从0开始
int end = right-1;
int key = array[end];//选取最右边的元素为基准值
while (begin < end)
{
//让begin从前往后找,找比基准值大的元素
while (begin < end && array[begin] <= key)
begin++;
//让end从后往前找,找比基准值小的元素
while (begin < end && array[end] >= key)
end--;
//找到后交换两个元素的值
if (begin < end)
{
Swap(&array[begin], &array[end]);
}
}
//交换begin(或者end)位置元素与基准值array[rigt-1]的值
if (begin == end)
{
Swap(&array[begin], &array[right - 1]);
}
2.2挖坑法
循环以下操作:(当begin=end时循环结束)
将基准值挖走,end位置形成一个坑
1.让begin从前往后找,找比基准值大的元素,然后用begin位置上的元素填end位置上的坑,begin位置又形成了新坑
2.让end从后往前找,找比基准值小的元素,然后用end位置上的元素填begin位置上的坑,end位置又形成了新坑
当循环结束之后,用key填充最后一个坑
最后返回begin
int Partion2(int array[], int left, int right)
{
int keyIndex = GetMiddleIndex(array, left, right);
Swap(&array[keyIndex], &array[right - 1]);
int begin = left;//begin不能从0开始
int end = right - 1;
int key = array[end];//end位置现在就是一个坑
while (begin < end)
{
//让begin从前往后找比基准值大的元素
while (begin < end && array[begin] <= key)
begin++;
//找到比基准值大的元素后,把该元素赋值给end位置的元素,填掉end位置的坑
//end-- 往前找比基准值小的元素
if (begin < end)
{
array[end] = array[begin];
end--;
}
//begin位置为一个新的坑位
//end从后往前找比基准值小的元素,找到后将该元素赋值给begin位置的元素,填begin的坑
while (begin < end && array[end] >= key)
end--;
if (begin < end)
{
//填掉begin位置的坑
//begin++继续往后找比基准值大的
array[begin] = array[end];
begin++;
}
//end现在又是一个新坑
}
//最后需要用基准值填最后一个坑位
array[begin] = key; //begin 与 end相等
return begin;
}
2.3前后指针
循环以下操作:
1.让cur从左至右找比基准值小的元素
2.找到之后如果prev的下一个位置和cur的位置不相等,则交换两个位置的元素,若相同cur继续往后找,不交换
找完整个数组后,若prev的下一个位置不在基准值的位置,则将prev下个位置的元素与基准值交换
最后返回prev
//3.前后指针
int Partion3(int array[], int left, int right)
{
int cur = left;
int prev = cur - 1;
int keyIndex = GetMiddleIndex(array, left, right);
Swap(&array[keyIndex], &array[right - 1]);
int key = array[right - 1];//选最右边元素为基准值
while (cur < right)
{
//让cur从前往后找,找比及准准小的元素
//若没找到,cur则继续往后找
//找到之后,如果prev的下一个位置和cur的位置不相等则交换两个位置的元素,若相同,cur继续往后找,不交换
if (array[cur] < key && ++prev != cur)
{
Swap(&array[cur], &array[prev]);
}
++cur;
}
//找完整个数组后,若prev的下一个位置不在基准值的位置,则将prev下个位置的元素与基准值交换
if (++prev != right - 1)
{
Swap(&array[prev], &array[right - 1]);
}
return prev;//返回基准值的下标
}
排序结果展示:
3.快速排序优化
1.三数取中法选key(array[left]、array[mid]、array[right-1]中取中间值)
int GetMiddleIndex(int array[], int left, int right)
{
int mid = left + ((right - left) >> 1);
if (array[left] < array[right-1])
{
if (array[mid] < array[left])
return left;
else if (array[mid] > array[right-1])
return right-1;
else
return mid;
}
else
{
if (array[mid] < array[right - 1])
return right - 1;
else if (array[mid]>array[left])
return left;
else
return mid;
}
}
2.递归到小的子区间时,可以考虑插入排序
4.快速排序非递归
void QuickSortNor(int array[], int size)
{
Stack s;
StackInit(&s);
StackPush(&s, size);
StackPush(&s, 0);
while (!StackEmpty(&s))
{
// 获取区间的右边界
int left = StackTop(&s);
StackPop(&s);
// 获取区间的左边界
int right = StackTop(&s);
StackPop(&s);
// [left, right)中的区间进行划分
if (right - left <= 1)
continue;
int div = Partion(array, left, right);
// 基准值的左侧[left, div)
// 基准值的右侧[div+1,right)
// 将基准值的右侧压栈
// 先压右侧然后再压区间的左侧
StackPush(&s, right);
StackPush(&s, div + 1);
// 将基准值的左侧压栈
// 先压右侧然后再压区间的左侧
StackPush(&s, div);
StackPush(&s, left);
}
StackDestroy(&s);
}
5.快速排序特性总结:
1. 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(logN)
最坏情况:当每次取到的基准值是最小或者最大元素时(或者接近最大或最小),时间复杂度为O(N^2)
4. 稳定性:不稳定