快速排序的讲解和排序算法时间复杂度的一个bug

之前一直使用STL中的sort来完成O(nlogn)的排序。忽然发觉还不会快速排序,经过一番学习有几点心得。

市面上常见到的排序算法一共有九种,按类可划分为三大类:

1.选择排序,冒泡排序,插入排序

2.快速排序,归并排序,堆排序

3.计数排序,基数排序,桶排序

前两类是基于比较的排序算法,通俗来说就是就是通过比较元素的大小进行排序。前者为O(n*n),后者O(nlongn)。但这只是粗略的估算,因为前者在一些数据下也可以是O(nlogn),而后者如果基准元素找的不好也可能退化为O(n*n)(以快速排序为例后面着重说)。第三类则是使用按按位划分,位映射的思想来排序,此时时间复杂度不仅和n的大小有关,还与元素的值的大小有关,局限性较大。但比如桶的思想在除排序之外的其他地方也有应用。由于第一类较慢,第三类局限性大,下面着重讲解第二类中最具代表性的快速排序。

传送门

快速排序的思想基于分治,顾名思义是分而治之。我们现在一个序列(标号是l~r)中随机找一个基准元素,比如可以找序列中的第一个数a[l]记为x,然后扫描这个序列,遇到比x小的放入数组b中,和x一样大的放入数组c中,比x大的放入数组d中。此时将bcd数组按顺序放入a数组中,前bb个数均比x小,后dd个数均比x大,我们只需再排这两个序列即可,方法和上面一样。下面有一个魔鬼细节,如果序列为1 2 3 4 5........100000,那么会超时。因为我们每次基准元素找的都是第一个,那么b序列没有数,c序列只有一个数,d序列有n-1个,那么需要循环n-1次,每次平均扫描n>>1个数,时间复杂度退化到n*n。可见面对一些极为特殊的数据无法通过,而一些比赛就喜欢出这种极端的数据,因此我们可以在l~r中随机取数避免这种情况。

#include <bits/stdc++.h>
using namespace std;
const int N=100001;
int n,a[N],b[N],c[N],d[N];
void qsort(int l,int r)
{
	if(l>=r) return ;
	int x=a[(l+r)>>1],bb=0,cc=0,dd=0;
	for(int i=l;i<=r;i++)
	{
		if(a[i]<x) b[++bb]=a[i];
		if(a[i]==x) c[++cc]=a[i];
		if(a[i]>x) d[++dd]=a[i];
	}
	for(int i=1;i<=bb;i++)
	a[i+l-1]=b[i];
	for(int i=1;i<=cc;i++)
	a[i+l+bb-1]=c[i];
	for(int i=1;i<=dd;i++)
	a[i+l+bb+cc-1]=d[i];
	qsort(l,l+bb-1);
	qsort(l+bb+cc,r);
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	qsort(1,n);
	for(int i=1;i<=n;i++)
	printf("%d ",a[i]);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值