算法刷题(3)——快速排序

166 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

今天去登录了一个好久都没有浏览过的刷题网站——ACWing,然后看到里面有算法基础课的活动,脑子一热就花了一百多块钱报了一个。其实我一直都有想要系统联系算法的打算,但总觉得自己难以坚持,所以这次被迫“忍痛割爱”,想要真正通过练习刷题来提升自己的算法水平。好了,废话不多说,我们开始今天的刷题之旅。

今天我们主要练习的是快速排序的基础算法,练习题目如下:

题目

image.png

输入格式

输入共两行,第一行包含整数 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的编译器上会显示超出时间限制。

总结

快速排序算法其实也是一种经典的排序算法,当真正理解其原理之后也会发现其实并不难。之后的文章我也将持续介绍其他的一些排序方法。好的,今天的刷题就到这里,我们下次见~