本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记。所有内容均来自MIT公开课Introduction to Algorithms中Charles E. Leiserson和Erik Demaine老师的讲解。(http://v.163.com/special/opencourse/algorithms.html

第四节-------快速排序 Quicksort

这节课的主要内容分为两部分,一部分是介绍快速排序算法,分析其在最好、最坏以及最好最差交替出现情况下的算法效率;另一部分则是介绍随机化快排算法,以及分析其算法复杂度。后者也是视频中最精彩的部分。

一、快速排序

快速排序由Tony Hoare在1962年提出,是基于分治思想(Divide-and-conquer)的一种原地排序,但其效率依赖于输入数据的排序状况。所谓原地排序,即指算法的空间复杂度为O(1),它就在原来的数据区域内进行重排。就像插入排序一样,就在原地完成排序。但是归并排序就不一样,它则需要额外的空间来进行归并排序操作。

如下图所示是快速排序中的分治法思想。

由上图可以知道,快速排序里面最关键的就是第一步:分化(Partition)的步骤,这是该算法处理问题的核心。所以我们可以把快排看成是递归地划分数组,就想归并排序是递归地合并数组一样。关于Paritition的具体算法,目前有好几种,其伪代码稍有不同但是它们的原理都是一样的。当然最重要的一点是它们的算法复杂度都是线性的,也就是O(n)。下面是快排的递归伪代码。

当然上面只是一个核心的递归代码。一个让快排运行更快的方法时调整这里的代码,找到适合元素数目较少的特别的算法。比如说,只剩下5个元素,你已经知道了一些代码来高效地排序5个元素,那么用那个特别的算法来替代这里的递归就会好一些。还有一些其他的办法,譬如说这是尾递归的代码,那么就可以使用一些尾递归的优化等等。。。

接下来分析快排的算法效率,在分析中假设所有的元素都是不同的。当重复元素存在时,这里的代码运行的就不是很好,但是Hoare最初的分划方法在待排序的元素中有重复的情况下更为高效。有时间的时候去看一下那个方法,它使用一个更加复杂的不变量来实现分划过程,但是本质上是一样的。

1、最坏情况下分析

那怎样的情况算是快速排序的最坏情况呢?当然是当输入序列是正序或者是反序的时候,因为这时候分划总是围绕着最大的或者是最小的元素进行,那么造成的现象就是分划的另外一边总是没有元素,这样就无法利用递归来提高算法运算的效率。

那么,在这种情况下,快排的算法效率递归式如下图所示,易知这时的效率是Θ(n^2)。

2、最优情况下分析

我们当然没有办法保证输入序列能够满足快排的最优情况,但是我们可以这样直观地来理解:如果我们足够幸运,Partition每次分划的时候正好将数组划分成了两个相等的子数组。那么这种情况下的递归分析式如下图所示。

这显然是我们想要的结果。那么当Partition的时候达不到均等地划分,如果两个子数列划分的比例是1:9呢?

为了解这个递归式,主方法是派不上用场了。这时候搬出总是能够有效的分析树,如下图所示。

综上所述,可以知道快排在最优情况下的算法效率是Θ(nlgn)。

3、最坏与最优交替出现情况下分析

上面我们分析了最好与最坏情况下的算法效率,那么更加普遍的情况是,当最坏情况与最优情况同时都有出现时的算法效率呢?我们假设算法中最坏与最优情况交替出现,那么算法的效率分析如下图所示。

可以得知,在这样的情况下我们也还是幸运的,得到的算法效率与最优情况下的效率一样。那么我们如何保证我们总是幸运的呢?这也是随机化快速排序需要解决的问题。

二、随机化快速排序

我们已经知道,若输入本身已被排序,那么对于快排来说就糟了。那么如何避免这样的情况?一种方法时随机排列序列中的元素;另一种方法时随机地选择主元(pivot)。这便是随机化快速排序的思想,这种快排的好处是:其运行时间不依赖于输入序列的顺序。也就是说我们不再需要对输入序列的分布作任何假设,没有任何一种特定的输入序列能够使算法的效率极低。最不幸运的情况有可能会发生,但是也是因为随机数产生器的原因,不是因为输入序列的原因。

这里的办法是随机地选择主元。如下图所示是随机化快速排序的算法效率递归表达式。

其中,X(k)是一个指示器随机变量,用来简化上述递归式中的复杂度。

因此,计算随机化算法的算法效率就演变成了计算上述含有指示器随机变量的递归式的期望值,具体过程如下图所示。

得到如上图所示的递归式后,如何求解成了一个问题。这里用的方法是替换法(Substitution method)。

这样,就得到了随机化快排的算法效率是Θ(nlgn)。

在实践中,快速排序是一个很好的算法。它虽然不能保证归并排序所提供的在最坏情况下nlgn的运行时间,但是在实践中如果采用随机化快速排序算法,通常比归并排序快3倍以上。当然不可否认的是,确实需要一些编码技巧来使它打到那样的速度,你需要优化基础情况以及其他的一些技巧,但是大部分好的算法都是基于快速排序的。

快速排序很好的另一个原因是它在虚拟内存的缓存中性能比较好。

关于Introduction to Algorithms更多的学习资料将继续更新,敬请关注本博客和新浪微博Sheridan

MIT算法导论——第四讲.Quicksort的更多相关文章

  1. MIT算法导论——第一讲.Analysis of algorithm

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  2. MIT算法导论——第二讲.Solving Recurrence

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  3. MIT算法导论——第五讲.Linear Time Sort

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  4. MIT算法导论笔记

    详细MIT算法导论笔记 (网络链接) 第一讲:课程简介及算法分析 (Sheridan) 第二讲:渐近符号.递归及解法  (Sheridan) 第三讲:分治法(1)(Sheridan) 第四讲:快排及随 ...

  5. MIT算法导论——第三讲.The Divide-and-Conquer

    本栏目(Algorithms)下MIT算法导论专题是个人对网易公开课MIT算法导论的学习心得与笔记.所有内容均来自MIT公开课Introduction to Algorithms中Charles E. ...

  6. MIT算法导论课程

    http://open.163.com/movie/2010/12/G/F/M6UTT5U0I_M6V2T1JGF.html

  7. quickSort算法导论版实现

    本文主要实践一下算法导论上的快排算法,活动活动. 伪代码图来源于 http://www.cnblogs.com/dongkuo/p/4827281.html // imp the quicksort ...

  8. [算法导论]quicksort algorithm @ Python

    算法导论上面快速排序的实现. 代码: def partition(array, left, right): i = left-1 for j in range(left, right): if arr ...

  9. 背包四讲 (AcWing算法基础课笔记整理)

    背包四讲 背包问题(Knapsack problem)是一种组合优化的NP完全问题.问题可以描述为:给定一组物品,每种物品都有自己的重量和价格,在限定的总重量内,我们如何选择,才能使得物品的总价格最高 ...

随机推荐

  1. js 如何获取文本框中光标索引位置

    function getTxt1CursorPosition(){ var oTxt1 = document.getElementById("txt1"); var cursurP ...

  2. JS一些语法

    1.解构(ES6的语法) 我个人理解就是有一个对象,对象里有几个属性,然后在定义新的变量的时候可以直接指定为和对象里属性名一样的名字,然后就可以关联到新的变量上来.下面看一个小测试例子: //解构 l ...

  3. Hadoop上路-01_Hadoop2.3.0的分布式集群搭建

    一.配置虚拟机软件 下载地址:https://www.virtualbox.org/wiki/downloads 1.虚拟机软件设定 1)进入全集设定 2)常规设定 2.Linux安装配置 1)名称类 ...

  4. Flex xxx-app.xml配置

    <?xml version="1.0" encoding="UTF-8"?>  <application xmlns="http:/ ...

  5. Java中的main()方法详解

    在Java中,main()方法是Java应用程序的入口方法,也就是说,程序在运行的时候,第一个执行的方法就是main()方法,这个方法和其他的方法有很大的不同,比如方法的名字必须是main,方法必须是 ...

  6. uImage、zImage、bzImage、vlinzx区别

    在网络中,不少服务器采用的是Linux系统.为了进一步提高服务器的性能,可能需要根 据特定的硬件及需求重新编译Linux内核.编译Linux 内核,需要根据规定的步骤进行,编译内核过程中涉及到几个重要 ...

  7. kendo ui template的用法

    kendo ui template的用法: Kendo UI 框架提供了一个易用,高性能的JavaScript模板引擎.通过模板可以创建一个HTML片段然后可以和JavaScript数据合并成最终的H ...

  8. div+css登陆界面案例

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  9. iOS - 指定视图的圆角个数-b

    平常设置视图的圆角最普遍的就是设置四个角的,方法也就是一句代码解决: view.layer.cornerRadius = 10; 四个圆角 但有时需求会是指定某个,或者特定哪几个角设置圆角,所以我们需 ...

  10. NPOI读取Excel数据应用

    NPOI 是 POI 项目的 .NET 版本.使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 WORD/EXCEL 文档进行读写.NPOI是构建在POI 3.x版本之上的,它 ...