AcWing算法专题——快速排序
文章目录
- AcWing算法专题——快速排序
- 前言
- 一、快速排序的核心
- 二、算法图示
- 三、算法代码
- 四、算法详解
- 1.递归停止的条件
- 2.下标移动的实现
- 3.下标初始值的设定
- 4.边界情况分析
- 4.1区间划分
- 4.2 do while循环条件
- 五、思考题
- 总结
前言
现在我们开始进入算法模块,在分享数据结构的知识的同时开始分享编程的算法,欢迎大家交流,有不对的地方欢迎大家斧正,也希望大家集思广益,拓展思维。
一、快速排序的核心
快速排序的核心思想是——分治。
何为分治,也就是说将一个完整的区间分成两个区间,这两个区间不一定等分,但快速排序算法的难点不在于怎么分治,在于边界问题的处理,如果边界问题处理不到位很有可能会出现各种各样的错误,比如陷入死循环,算法难度不大,但是有一些细节问题还是得注意不然就会运行失败。
二、算法图示
如果满足q[i]<x,那就说明i这个位置放的是对的,就让i向后移动,直到不满足条件停止
同理j向前移动,不满足的时候停止,两个都停止了就交换两个的位置,然后判断递归是否停止,如果不停止继续递归
三、算法代码
void quick_sort(int q[], int l, int r)
{
//递归的终止情况
if(l >= r) return;
//第一步:分成子问题
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while(i < j)
{
do i++; while(q[i] < x);
do j--; while(q[j] > x);
if(i < j) swap(q[i], q[j]);
}
//第二步:递归处理子问题
quick_sort(q, l, j), quick_sort(q, j + 1, r);
//第三步:子问题合并.快排这一步不需要操作,但归并排序的核心在这一步骤
}
四、算法详解
1.递归停止的条件
我们设置了两个特殊的端点,左端点和右端点,如果左端点大于等于右端点就说明我们所给数组只有一个元素无需排序或者已经递归结束,无论那种情况都要递归停止。
2.下标移动的实现
我们采用do while这种循环方式来实现,用其他方式也可以做到,但是这里建议用do while更加直观。
3.下标初始值的设定
因为我们不可能直接去移动左右端点的下标,这样会影响我们后续的操作,所以我们设置了两个变量 i 和 j来存储下标,我们采用的do while循环的特点是先执行后判断,所以我们初始条件就得是左端点的前一个和右端点的后一个位置,如下图:
4.边界情况分析
4.1区间划分
快排属于分治算法,最怕的就是 n分成0和n,或 n分成n和0,这会造成无限划分
1.用i划分,x不可以选择q[l]。
2.用j划分,x不可以选择q[r].
上述两种情况会造成无限划分。
关键句子quick_sort(q, l, j), quick_sort(q, j + 1, r);
由于j的最小值是l,所以q[j+1…r]不会造成无限划分
但q[l…j](即quick_sort(q, l, j))却可能造成无限划分,因为j可能为r
举例来说,若x选为q[r],数组中q[l…r-1] < x,这一轮循环结束时i = r, j = r,显然会造成无限划分
4.2 do while循环条件
do i++; while(q[i] < x)和do j–; while(q[j] > x)不能用q[i] <= x 和 q[j] >= x
假设q[l…r]全相等,则执行完do i++; while(q[i] <= x);之后,i会自增到r+1,然后继续执行q[i] <= x 判断条件,造成数组下标越界,之后的q[i] <= x (此时i > r) 条件也成立,会超出内存。
五、思考题
大家可以在评论区讨论一下快速排序的时间复杂度是多少?
答案下期公布哦~
总结
快排总结:
1.确定数组q,确定左端点l,确定右端点r。
2.确定划分边界x
3.按照划分的边界x将数组分成<=x,>=x的两个区间。
4.分别递归处理两个区间。
快排定下划分边界x后就从左右端点位置开始向中间走,两个区间中不满足就停下,同时停下了就交换数据然后继续向下走,直到两个下标相等了或者左边区间的下标大于右边区间的下标了就停止,也就完成了快排。
如果大家还有什么其他思路欢迎大家交流,这个模板也有可以改进的地方,欢迎大家来斧正~