快速排序
快速排序的基本思想是基于分治策略的,其算法思想如下:
1.分解:先从数列中取出一个元素作为基准元素看,以基准元素为标准,
将问题分解为两个子序列,使小于或等于基准元素的子序列在左侧,使大于基准元素的子序列在右侧。
2.治理:对两个子序列进行快速排序。
3.合并:将排好序的两个子序列合并在一起,得到原问题的解。
(1)分解:
选取第一个数作为pivot,设置两个指针low,high,意思即为R[low:high]
指针从左开始扫描,若是R[low]<=pivot,则指针low右移,若遇到R[low]>pivot,R[low]的值和R[high]的值交换,
同时,high指针向左移动,此时继续比较low指针与pivot大小关系。重复上述动作,这样一来,R[low]和pivot
每比较一次就会有指针移动,要么是low右移要么是high左移,最终的结果一定是low和high重合。
返回该位置mid=i;该位置正好是pivot元素。上代码:
int partition(int r[],int low,int high)
{
int i=low,j=high,pivot=r[low];
while(i<j)
{
if(r[i]<pivot)
i++;
else
swap(r[i],r[j--]);
}
swap(r[low],r[j]);
return j;
}
当然,还有另一种思路:
int Partition(int r[],int low,int high)
{
int i=low,j=high,pivot=r[low];
while(i<j)
{
while(i<j&&r[j]>pivot)
{
j--;
}
if(i<j)
{
swap(r[i++],r[j]);
}
while(i<j&&r[i]<=pivot)
{
i++;
}
if(i<j)
{
swap(r[i],r[j--]);
}
}
return j;
}
两中方法结果是一样的。
2.快速排序递归算法:
首先对原序列执行划分,得到划分的中间位置mid,然后以中间位置为边界,分别对左半部分(low,high-1)执行快速排序,右半部分(mid+1,high)执行快速排序。递归条件是low>=high.
void QuickSort (int R[],int low,int high)
{
int mid;
if(low<high)
{
mid=Partition(R,low,high);//返回基准元素位置
QuickSort(R,low,mid-1);//左区间递归快速排序
QuickSort(R,mid+1,high);//右区间递归快速排序
}
}
快速排序算法平均情况下,时间复杂度为O(nlogn),递归调用所使用的栈空间也是O(logn);
优化算法:
int Partition2(int r[],int low,int high)
{
int i=low,j=high,pivot=r[low];//基准元素
while(i<j)
{
while(i<j&&r[j]>pivot) i--;//向左扫描
while(i<j&&p[i]<=pivot) i++;//向右扫描
if(i<j)
{
swap(r[i++],r[j--]);
}
}
if(r[i]<pivot)
{
swap(r[i-1],r[low]);
return i-1;
}
swap(r[i],r[low]);
return i;
}
进一步优化:因为以上快速排序算法每次只能返回一个值,比如上一个算法返回的是i,那么如果一次返回多个值,会不会更快呢。
public int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while (l < more) {
if (arr[l] < arr[r]) {
swap(arr, ++less, l++);
} else if (arr[l] > arr[r]) {
swap(arr, --more, l);
} else {
l++;
}
}
swap(arr[more],arr[r]);
return int[] { less + 1, more };
}