快速排序,简称快排,常称QuickSort、QSort。在排序算法中非常常用,其编程复杂度低,时间复杂度O(NlogN),空间复杂度O(N),执行效率稳定,而且常数很低。

基本思想就是二分,例如你要将N个数排序,你调用了QSort(1,N)。那么快排会这样做:

1、找出一个数x

2、将N个数分成两部分,左边的都比x小,右边的都比x大

3、分别对两边调用QSort

很显然,这是二分,递归实现。

  先说第二步,代码别写得太难看,时间复杂度就是O(N),扫一遍就可以了。于是,重点便是第一步——我们假设你找x的时间是O(1),那么如果你的x每次找到的都是中位数,那么算法时间就是O(NlogN);如果你的x每次找到的都是最边上的数(以至于你将N个数分成了1个和N-1个),那么算法时间就是O(N²)。因此,只有在优秀的选x方法下,快排才能保证O(NlogN)的复杂度。

  我们来详细讨论一下第一步(下面分析“中位数”的实际含义,以及给出两种常见实现取法,高手可以跳过直接去看代码了~)。

  我们理想情况是找中位数,但是你不可能真正去找中位数,因为那样的时间是O(N)。新手很头疼,“这咋整?”方法很简单:随便选一个就好了。

  新手更头疼了……“你随便选一个,选的时间当然是O(1)了,可你凭什么保证算法复杂度不退化?”其实,我也不能保证算法不退化,但我知道从概率上说,我每次都随机选,大部分时候都没退化多少,结果就只有很低很低的概率退化成O(N²)。新手很鄙视,“如果我完全可以给你构造出一组数据,让你每次都选边上的啊!这样不就退化成O(N²)了吗? ”这点其实是不可能的。因为我不是固定选某个位置的数,而是随机选,所以你根本无法构造,我退化多少,只取决于概率。

  新手要放弃了……“你这快排复杂度直接取决于概率,可我概统没学好,也不知道快排的退化概率是多少,我怎么敢用啊!万一我用的时候正好退化了咋办!”这点,便是我今天要重点和广大新手说的,你们接触到了一个算法中很重要的概念:随机算法。

  算法复杂度,只是对算法一个很粗的描述。你知道一个算法的复杂度是O(N²),其实你只是知道它是两阶而已,根本不知道真正的复杂度。复杂度的常数是多少?是3N²、0.3N²,还是1.3N²?平时我们不分析,是因为我们都按照最大复杂度分析的。题目给你N=1000,你知道算法复杂度O(N²),又知道常数很大时(例如100)程序不到1s可以运行完,于是你便敢写了。可是现在不行了,算法是随机的,好的时候O(NlogN)常数还很小,坏的时候O(N²)常数还很大,你还敢不分析?

  可能有人不敢用,觉得只要是概率就不能保证没问题,万一考试碰上就惨了。这种思想一般都是新手才会有,请你务必说服自己!我的理由很简单,概率太高我也不敢用,我的做法是,把概率降到比你某天出门被花盆意外砸死的概率还要低,我就敢用了,因为我确信我不会某天出门被花盆砸死。

  当然,明确了这一点,现在的问题就是,不会分析怎么办?长远来看,你还是回去好好学学概率,再回来分析得好;短期来看,有没有简单些的方法呢?当然有,就是测试。你随机出很多很多数据,用你写的快排去测,发现他们最惨的也完全可以算作O(NlogN),那基本就没问题了,因为实际考试和实际应用数据情况也基本是这样。

  好了,说了这么多,其实只是因为理解快排的思想是很多新手的一道门槛。我希望能通过自己多说些废话,帮助很多新手顺利迈过去。这样,对很多新手以后的算法之路都是有益无害的。下面让我们讨论第一步实际应当如何做:

  必须明确,如果选择方法过于复杂,那么算法常数会变大;如果方法过于简单,那么算法复杂度会退化。因此综合考虑,加以分析和大量实测,比较常见的既好写又快的写法有两种。假设你有N个数A[1~N]:

1、x=mid(A[1],A[(1+N)/2],A[N]),mid是指取这三个数的中位数。这是最常用的一种方法,如果我没记错的话,这种算法也是C++算法库(algorithm)里面的写法。实际情况表明,这种取法效率很高。

2、x=A[randint(1,N)],也就是下标取1~N中随机一个数。这也是比较常用的一种方法,好处是真正保证了随机性,但坏处是生成随机数耗时比较高,会导致算法常数变大。

说了这么多,新手可能会觉得我还是没说明为什么快排的复杂度是O(NlogN)。我只能说,要分析快排复杂度需要很细的分析和大量的数据,有机会我会单独写一篇文章来分析的,现在我只能从概率上告诉你大部分时候都是O(NlogN),而且快排常数比堆排小不少(时间大概快一倍吧,没实测过,瞎说),能卡快排的数据你也暂时遇不到。我不敢说没有数据能卡快排,但我可以确定,如果不是特意要卡你,这样写快排一定没问题,反正我考试是敢用的。要是真有人死活卡你,那你就写堆排吧,常数大点,但确实不可能被卡。

下面给出我的代码:

 inline void swap(int &a,int &b) { int t=a; a=b; b=t; }

 inline int mid(int a,int b,int c)
{
if(a>b) swap(a,b);
if(b>c) swap(b,c);
if(a>b) swap(a,b);
return b;
} void QSort(int A[],int l,int r) // 升序
{
if(l>=r) return;
int i=l,j=r,x=mid(A[l],A[(l+r)>>],A[r]);
while(true)
{
while(i<=r && A[i]<x) ++i;
while(l<=j && A[j]>x) --j;
if(i>j) break;
swap(A[i],A[j]); ++i; --j;
}
QSort(A,l,j); QSort(A,i,r);
}

《算法》C++代码 快速排序的更多相关文章

  1. Python实现各种排序算法的代码示例总结

    Python实现各种排序算法的代码示例总结 作者:Donald Knuth 字体:[增加 减小] 类型:转载 时间:2015-12-11我要评论 这篇文章主要介绍了Python实现各种排序算法的代码示 ...

  2. php四种排序算法实现代码

    分享php排序的四种算法与代码. 冒泡:function bubble_sort($arr){ $num = count($arr); for($i=0;$i<$num;$i++){ for($ ...

  3. <算法笔记>关于快速排序的算法优化排序(顺便给百度百科纠个错)

    快速排序是排序算法之中的基本中的基本,虽然越来越多的接口函数将快速排序“完美的封装了起来”,比如C++中的qsort或者<algorithm>中的sort(与stable_sort相对应) ...

  4. 10个经典的C语言面试基础算法及代码

    10个经典的C语言面试基础算法及代码作者:码农网 – 小峰 原文地址:http://www.codeceo.com/article/10-c-interview-algorithm.html 算法是一 ...

  5. 经典面试题(二)附答案 算法+数据结构+代码 微软Microsoft、谷歌Google、百度、腾讯

    1.正整数序列Q中的每个元素都至少能被正整数a和b中的一个整除,现给定a和b,需要计算出Q中的前几项, 例如,当a=3,b=5,N=6时,序列为3,5,6,9,10,12 (1).设计一个函数void ...

  6. 排序算法Java代码实现(一)—— 选择排序

    以下几篇随笔都是记录的我实现八大排序的代码,主要是贴出代码吧,讲解什么的都没有,主要是为了方便我自己复习,哈哈,如果看不明白,也不要说我坑哦! 本片分为两部分代码: 常用方法封装 排序算法里需要频繁使 ...

  7. 【转】Algorithms -离散概率值(discrete)和重置、洗牌(shuffle)算法及代码

    离散概率值(discrete) 和 重置\洗牌(shuffle) 算法 及 代码 本文地址: http://blog.csdn.net/caroline_wendy/article/details/1 ...

  8. k-近邻算法python代码实现(非常全)

    1.k近邻算法是学习机器学习算法最为经典和简单的算法,它是机器学习算法入门最好的算法之一,可以非常好并且快速地理解机器学习的算法的框架与应用.它是一种经典简单的分类算法,当然也可以用来解决回归问题.2 ...

  9. Adaboost算法及其代码实现

    . . Adaboost算法及其代码实现 算法概述 AdaBoost(adaptive boosting),即自适应提升算法. Boosting 是一类算法的总称,这类算法的特点是通过训练若干弱分类器 ...

  10. 机器学习算法及代码实现–K邻近算法

    机器学习算法及代码实现–K邻近算法 1.K邻近算法 将标注好类别的训练样本映射到X(选取的特征数)维的坐标系之中,同样将测试样本映射到X维的坐标系之中,选取距离该测试样本欧氏距离(两点间距离公式)最近 ...

随机推荐

  1. AD的命名规则 AD常用产品型号命名规则

    AD的命名规则 AD常用产品型号命名规则 DSP信号处理器    放大器工业用器件通信    电源管理    移动通信 视频/图像处理器等 模拟A/D    D/A 转换器 传感器    模拟器件 A ...

  2. android+nutz后台如何上传和下载图片

    android+nutz后台如何上传和下载图片  发布于 588天前  作者 yummy222  428 次浏览  复制  上一个帖子  下一个帖子  标签: 无 最近在做一个基于android的ap ...

  3. poj 3485 区间选点

    题目链接:http://poj.org/problem?id=3485 题意:X轴上公路从0到L,X轴上下有一些点给出坐标代表村庄,问在公路上最少建几个出口才能使每个村庄到出口的距离不超过D. 以村庄 ...

  4. 广搜破解密码(HDU1195)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1195 解题报告: #include<iostream> #include<cstdi ...

  5. http知识补充

    在我的职业生涯中,没怎么重视过这http四个字,想当然的觉得不就是个网页请求嘛就没怎么当回事,而且很多http相关的长篇大论一听就困,真心是弄不下去,但是就是这种观念导致我后期的工作中不断的挖坑,不断 ...

  6. BZOJ 2824: [AHOI2012]铁盘整理

    BZOJ 2824: [AHOI2012]铁盘整理 标签(空格分隔): OI-BZOJ OI-搜索 Time Limit: 10 Sec Memory Limit: 128 MB Descriptio ...

  7. P1151 子数整数

    题目描述 对于一个五位数a_1a_2a_3a_4a_5a1​a2​a3​a4​a5​,可将其拆分为三个子数: sub_1=a_1a_2a_3sub1​=a1​a2​a3​ sub_2=a_2a_3a_ ...

  8. 重设SVN 的GNOME keyring [(null)] 的密码

    在ubuntu里如果改了登录密码,那么在使用svn是会要求GNOME keyring [(null)] 的密码: 去掉这个只需:rm -f ~/.gnome2/keyrings/login.keyri ...

  9. C语言函数申明关键字inline

    内联inline是给编译器的优化提示,如果一个函数被编译成inline的话,那么就会把函数里面的代码直接插入到调用这个函数的地方,而不是用调用函数的形式.如果函数体代码很短的话,这样会比较有效率,因为 ...

  10. iclr2015

    http://www.iclr.cc/doku.php?id=iclr2015:main#accepted_papers iclr2015的accept papers,有些看过,有些没看明白,看来还是 ...