算法思想
找一个基准值,本题中基准值为数组中最右的元素,再定义两个指针begin(指向首元素)、end(指向尾元素);
begin从前往后走找比基准值大的元素,找到后停下;end从后往前走找比基准值小的元素,找到后也停下;然后,交换arry[begin]和arry[end],依次循环操作;
当begin与end相遇,将arry[begin]或arry[end]与基准值交换
代码实现(递归):
#include <stdio.h>
int PartSort(int* a,int right,int left)
{
int key = a[right];
int keyid = key;
while(right > left)
{
while(right > left || a[left] <= key)
++left;
while(left < right || a[right] >= key)
--right;
if(a[left] != a[right])
Qswap(&a[left],&a[right]);
}
Qswap(&a[left],&a[keyid]);
return left;
}
//递归实现
void QuickSort(int *a,int left,int right)
{
if(a == NULL || right < 0 || left < 0)
return 0;
if(left < right)
{
int div = PartSort(a,left,right);
QuickSort(a,left,div-1);
QuickSort(a,div+1,right);
}
}
算法优化
三数取中法
选择这组数据的第一个元素、中间元素、最后一个元素,这三个元素里面值居中的元素作为基准数;
代码实现:
#include<stdio.h>
int Max(int a,int b)
{
return a>b?a:b;
}
int Min(int a,int b)
{
return a>b?b:a;
}
int GetMidIndex(int* a,int left,int right)
{
if(a == NULL || left<0 || right < 0)
return 0;
int mid = left + (right - left)>>1;
int max1 = Max(a[left],a[right]);
int max2 = Max(a[mid],Min(a[left],a[right]));
int MidIndex = Min(max1,max2);
return MidIndex;
}
非递归实现:
#include<stdio.h>
#include<stack>
using namespace std;
class Sort
{
public:
void Quick_sort(int *arr,int len)
{
std::stack<int> Stack;
if(arr == NULL || len < 0)
return ;
int left = 0;
int right = len - 1;
int pivot = 0;
Stack.push(left);
Stack.push(right);
while(!Stack.empty())
{
int begin = Stack.top();
Stack.pop();
int end = Stack.top();
Stack.pop();
pivot = PartSort(arr,begin,end);
if(begin<pivot-1)
{
Stack.push(pivot-1);
Stack.push(left);
}
if(pivot + 1 < end)
{
Stack.push(end);
Stack.push(pivot+1);
}
}
}
};
复杂度分析
快速排序的时间代价很大程度上取决于枢轴的选择,最简单的办法就是选择第一个记录或者最后一个记录作为枢轴值,但这样做的弊端在于当原始的输入序列已经有序,每次分割会将剩余的记录全部分到一个序列当中,而另外一个序列为空。这种情况也是最差的情况(eg:第一趟经过n-1次比较,第一个记录定在原始位置,左半子序列为空,右半子序列为n-1个记录;第二趟n-1个经过n-2次比较,第2个记录定在原始位置,左半子序列仍为空,右半子序列为n-2个记录,以此类推,共需进行n-1趟排序,其比较次数为[(n-1)+(n-2)+......+(n-(n-1))]=n(n-1)/2=O(n^2)【时间复杂度取最高项,然后忽略掉其系数】;
最好的情况就是,每次分割都恰好将记录序列分为两个长度相等的子1序列。初始的n个记录序列,第一次分割为两个长度为n/2的子序列,第2次分割为4个长度为n/4的子序列,以此类推,总共需要分割logn次,所有分割步数之和为n,因此最终的时间复杂度为O(nlogn).
稳定性
因为快速排序每次移动记录的跨度比较大,因此快排是不稳定的。