关键词:排序、分治、双指针
地位:算法竞赛几乎不用(有sort函数);在考研是重中之重(有时要求你默写)。
这是基于分治法的排序算法。他的算法步骤主要如下:
1.确定分界点,取数组最中间的下标;(太左、太右会有TLE的风险)
2.调整区间,设步骤1取得的下标对应的元素为x,将区间分成两个部分,大于等于x和小于等于x;
3.递归处理左右两段即可。即对左右两段再次分治,一直细分,直到无法再细分。
(此处为初版的算法基础课,分界点可以自由选择,但建议取数组中点)
递归处理的方式如下:
设l为当前数组最左边的下标、r为最右边的下标。
i指针(下标)从l开始前进,j指针(下标)从r开始后退。
如果左指针对应的数小于x,i前进,直到遇到第一个大于等于x的数为止;
如果右指针对应的数大于x,j后退,直到遇到第一个小于等于x的数为止;
如果i<j,则a[i]与a[j]交换,之后在[l,j]、[j+1,r]的区间内分治排序即可。
(因为如果i≥j则这段区间一定是有序的)
直到最小的区间排序完后快速排序结束。
下面是快速排序的C++代码与个人注释(在y总的模板上稍作改进)
#include<iostream>
using namespace std;
const int N=1e5+10;//题目的n的取值范围最大可以到100000 1e几是10的几次方(double类型)
int a[N];//创建一个大小为100010的数组
void quickSort(int l,int r)//快速排序,从小到大排序
{
if(l>=r) return ;//如果l大于等于r则这段区间没有内容,递归结束
//y总的快速排序模板的指针要求初始往前/后移一格,防止出现边界问题
//(l+r)>>1是当前排序的数组中间的下标。+的优先级大于>>,为让读者
//易懂,加圆括号。>>1为二进制右移一格,相当于/2
int i=l-1,j=r+1,x=a[(l+r)>>1];
while(i<j)//整理i,j区间的数直到两个指针重合
{
//这里采用do while的形式,处理边界问题
//如果将这两个代码大小于号反过来写,则变为从大到小排序(见注释)
do i++;while(a[i]<x);//a[i]>x
do j--;while(a[j]>x);//a[i]<x
//swap是交换两个数的函数
//要判定是否i<j,因为这时候的i和j可能已经重合
if(i<j) swap(a[i],a[j]);
}
//分成两段区间继续快速排序
quickSort(l,j);
quickSort(j+1,r);
}
int main()
{
//处理输入输出、排序
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
quickSort(1,n);
for(int i=1;i<=n;i++) printf("%d ",a[i]);
return 0;
}
快速排序的平均时间复杂度是,但有一种极端情况:该序列接近于有序,例如1 2 3 4 ... 99996 99997 99999 99998 100000,交换的次数明显增多,快速排序退化为冒泡排序,时间复杂度是,是不稳定的排序,容易TLE。所以在算法竞赛中使用快速排序将是没有必要也不值得的选择,但快速排序算法使用的方法具有普世意义,在考研、面试中很经常遇到。
注:TLE:Time Limit Exceed,时间超限
参考链接/文献:
Ⅰ.AcWing——算法基础课,2019,闫学灿 活动 - AcWing
Ⅱ.AcWing785.快速排序 活动 - AcWing
感谢您的支持