携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
今天去登录了一个好久都没有浏览过的刷题网站——ACWing,然后看到里面有算法基础课的活动,脑子一热就花了一百多块钱报了一个。其实我一直都有想要系统联系算法的打算,但总觉得自己难以坚持,所以这次被迫“忍痛割爱”,想要真正通过练习刷题来提升自己的算法水平。好了,废话不多说,我们开始今天的刷题之旅。
今天我们主要练习的是快速排序的基础算法,练习题目如下:
题目
输入格式
输入共两行,第一行包含整数 n。
第二行包含 n 个整数(所有整数均在 1∼10的9次方范围内),表示整个数列。
输出格式
输出共一行,包含 n 个整数,表示排好序的数列。
数据范围
1≤n≤100000
输入样例:
5
3 1 2 4 5
输出样例:
1 2 3 4 5
解析
我们首先要了解什么是快速排序,快速排序其实质是一种分治算法,其流程是:
- 首先确定一个分界值,通过该分界值将数组分成左右两部分,分界值的选取一般是数组首尾元素或者中间元素。
- 调整分界值左右两端的区间,一般而言将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值
- 将左右两边的部分在进行快速排序(按照前面两步),这里可以用递归调用来实现。
如果想要详细理解快速排序,可以参考这篇文章: 快速排序法详解
按照这种思想,我们可以得到一个适用于快速排序的解体模板:
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);
}
上述的模板使用了do...while语句来实现指针的移动,这种方式也便于大家理解。 这里的递归调用其实也是对分治思想的体现,通过不停的对每一个分出来的部分进行再次排序,最后就会得到一个完全有序的结果。
代码实现
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int n,a[N];
/*快速排序模块*/
void quick_sort(int *a,int l,int r){
if(l>=r){ //元素是否为1以及是否结束递归的判断
return;
}
int x = a[l+r>>1],i = l-1,j = r+1;
/*首尾指针移动*/
while(i<j){
do i++; while(a[i]<x);
do j--; while(a[j]>x);
if(i<j){
swap(a[i],a[j]);
}
}
/*对左右部分再次排序,递归调用*/
quick_sort(a,l,j);
quick_sort(a,j+1,r);
}
int main(){
scanf("%d",&n);
for(int i=0;i<n;i++){
scanf("%d",&a[i]);
}
quick_sort(a,0,n-1);
for(int i=0;i<n;i++){
printf("%d ",a[i]);
}
return 0;
}
这里对代码部分进行一些特别的注明:
- l+r>>1指的是将l+r的值右移一位,获得的结果和(l+r)/2是一样的,不过这样会更快一些。
- 这里N赋值1e6 + 10,是按照题目要求给定数组的大小限制。
- 上述代码分界值取的是数组元素的中间值,而没有像算法数中所描述的那样去首尾值,其实理论上都可以,但是取中间值的话时间复杂度会低一些,取首尾值在ACwing的编译器上会显示超出时间限制。
总结
快速排序算法其实也是一种经典的排序算法,当真正理解其原理之后也会发现其实并不难。之后的文章我也将持续介绍其他的一些排序方法。好的,今天的刷题就到这里,我们下次见~