快速排序(Quicksort)是对冒泡排序的一种改进。
其基本思想是:选择一个基准,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比基准大,另外一部分的所有数据都要比基准小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
Partition()函数:
每次划分时,当满足判断条件low<high,
1.从high开始向前搜索,找一个比基准小的数数据,将其与基准交换,
2.然后从low开始向后搜索,找一个比基准大的数据,将其放到high的位置上,
重复1,2 直到不满足low<high跳出循环,将基准放在low=high的位置上,返回基准的新位置。
//快速排序的一次划分
int Partition(int *arr,int low,int high)//O(n)(时),O(1)(空)
{
int tmp = arr[low];//基准
while(low < high)
{
while(low<high && arr[high]>tmp)
{
high--;
}
arr[low] = arr[high];
while(low<high && arr[low]<tmp)
{
low++;
}
arr[high] = arr[low];
}
arr[low] = tmp;//low==high
return low;
}
Quick()函数:
做为内部函数是为了完成一次封装,
对排序划分的递归调用,
先处理基准左边的数据,调用划分,基准左右两边至少有两个数据,才会调用划分
static void Quick(int*arr,int low,int high)//low为首位数据的指针,high为末位数据的指针
{
int par = Partition(arr,low,high);
if(low+1 < par)//左边至少两个数据
{
Quick(arr,low,par-1);
}
if(par+1 < high)//右边至少两个数据
{
Quick(arr,par+1,high);
}
}
对快排的一次包装,使其只有两个参数,与其他排序的参数一致
void QuickSort(int *arr,int len)//O(nlogn),O(logn),不稳定
{
Quick(arr,0,len-1);
}
//非递归的快速排序
void QuickSort2(int *arr,int len)//O(n),O(logn),不稳定
{
SeqStack s;
InitStack(&s);
int low = 0;
int high = len-1;
int par = Partition(arr,low,high);
if(low+1 < par)
{
Push(&s,low);
Push(&s,par-1);
}
if(par+1 < high)
{
Push(&s,par+1);
Push(&s,high);
}
while(!IsEmpty(&s))
{
Pop(&s,&high);
Pop(&s,&low);
par = Partition(arr,low,high);
if(low+1 < par)
{
Push(&s,low);
Push(&s,par-1);
}
if(par+1 < high)
{
Push(&s,par+1);
Push(&s,high);
}
}
Destroy(&s);
}
对于优化
快排对于初始记录基本有序的情况下,快排与冒泡无异,
对于分治算法,当每次划分时,算法若都能分成两个等长的子序列时,那么分治算法效率会达到最大。也就是说,基准的选择是很重要的。选择基准的方式决定了两个分割后两个子序列的长度,进而对整个算法的效率产生决定性影响。
最理想的方法是,选择的基准恰好能把待排序序列分成两个等长的子序列
方法(1):随机选取基准(不重要)
引入的原因:在待排序列是部分有序时,固定选取枢轴使快排效率底下,要缓解这种情况,就引入了随机选取枢轴
//基准选区
srand((unsigned)time(NULL));
int pivotPos;//基准
pivotPos= rand()%(high - low) + low;
方法(3):三数取中(median-of-three)(优化有序的数据)
引入的原因:虽然随机选取枢轴时,减少出现不好分割的几率,但是还是最坏情况下还是O(n^2),要缓解这种情况,就引入了三数取中选取枢轴
。。。代码,之后再上