对于快速排序,与冒泡排序一样属于交换排序,但是快速排序是在冒泡排序的基础之上的优化,由于冒泡排序需要进行多次的交换,从而导致冒泡排序的效率相对较低,而快速排序应用了分治这一算法思想,通过设定基准值,用序列中的数据与这一基准值进行比较,从而达到将所有大于基准值的数据放到基准值一边,所有小于基准值的数据放到基准值另一边这一目的。从而将原序列以基准值为界,分成大于基准值与小于基准值这两部分,然后对于这分割后的两部分,分别进行之前的操作。
而这样一种将一个大问题分成若干个子问题,并且对于子问题之间相互独立,而子问题又可以进行划分成更小的子问题,直到可以简单解决对应的子问题,而也因此将子问题的解进行合并就可以得到原问题的解,这一思想就叫做分治,该算法的核心在于对于一个难以下手的问题,可以将它划分成若干个可以轻松解决的子问题,以便各个击破。
话说回来,对于快排而言,应用分治算法是一种不错的解决办法,但问题关键在于如何将所有大于基准值的数据放至到基准值的一边,而所有小于基准值的数据放到另一边,所以下面有两种方法:
1.挖坑法
设定头尾指针(索引)begin,end,并假设基准值为最右端的数据,保存基准值,并对其位置设坑,将begin,end位置上的值依次与基准值进行比较,若begin位置上的值大于基准值,则用这个值填上一个留下的坑,并对begin所在位置设新坑,而当end位置上的值若小于基准值,则用这个值填上一个留下的坑,而对于自己当前位置重新设新坑,注意若begin,end不满足前面两个各自的条件,则将begin与end分别向中间靠拢,直到begin不在小于end
代码如下:
int Partion1(int arr[],int left,int right)
{
int k=arr[right];
int begin=left;
int end=right;
while(begin<end)
{
while(begin<end&&arr[begin]<k)
begin++;
arr[end]=arr[begin];
while(begin<end&&arr[end]>k)
end--;
arr[begin]=arr[end];
}
arr[begin]=k;
return begin;
}
void QuickSort(int arr[],int left,int right)
{
if(left<right)
{
int div=Partion2(arr,left,right);
QuickSort(arr,left,div-1);
QuickSort(arr,div+1,right);
}
}
2.双指针法
这种方法则是设定两个指针pre,cur,pre表示小于基准值的最大数据的位置,而cur则是用来通过一次从左到右的遍历,将小于基准值的数据与pre对应的数据进行交换
代码如下:
int Partion2(int arr[],int left,int right)
{
int cur=left;
int pre=left-1;
int k=arr[right];
while(cur<=right)
{
if(arr[cur]<=k&&++pre!=cur)
{
swap(arr[cur],arr[pre]);
}
cur++;
}
return pre;
}
void QuickSort(int arr[],int left,int right)
{
if(left<right)
{
int div=Partion2(arr,left,right);
QuickSort(arr,left,div-1);
QuickSort(arr,div+1,right);
}
}
3.快速排序的非递归方法
对于快速排序的非递归方法而言,与之前的递归方法在对数据与基准值进行比较并进行交换这一点方法没什么区别,可以用挖坑法也可以用双指针法,唯一不同的在于通过栈来保存分割之后的子序列的left和right,从而进行循环
代码如下:
int Partion(int arr[],int left,int right)
{
int cur=left;
int pre=left-1;
int k=arr[right];
while(cur<=right)
{
if(arr[cur]<=k&&++pre!=cur)
{
swap(arr[cur],arr[pre]);
}
cur++;
}
return pre;
}
void quicksort(int arr[],int left,int right)
{
stack<int> s;
s.push(left);
s.push(right);
while(!s.empty())
{
right=s.top();
s.pop();
left=s.top();
s.pop();
if(left<right)
{
int div=Partion(arr,left,right);
s.push(div+1);
s.push(right);
s.push(left);
s.push(div-1);
}
}
}
对于快速排序而言,时间复杂度最优为O(NlogN),即通过基准值能将数据序列分为两个等长序列(类比二叉树),最差为O(N^2),即原序列本身就有序;空间复杂度为O(logN);且算法不稳定