热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【算法导论】排序快速排序

【转载自:http:blog.csdn.netshuangde800articledetails7599509】快速排序是最常用的一种排序算法,包括C的qsort,C++和Java

【转载自:http://blog.csdn.net/shuangde800/article/details/7599509】

快速排序是最常用的一种排序算法,包括C的qsort,C++和Java的sort,都采用了快排(C++和Java的sort经过了优化,还混合了其他排序算法)。

快排最坏情况O( n^2 ),但平均效率O(n lg n),而且这个O(n lg n)几号中隐含的常数因子很小,快排可以说是最快的排序算法,并非浪得虚名。另外它还是就地排序。

 

快速排序是基于分治模式的:

分解:数组A【p..r】被划分成两个(可能空)子数组A【p..q-1】和A【q+1..r】,使得A【p..q-1】中的每个元素都小于等于A(q),而且,小于等于A【q+1..r】中的元素。下 标q 也在返个划分过程中迕行计算。

解决:通过递归调用快速排序,对子数组A【p..q-1】和A【q+1..r】排序。

合并:因为两个子数组使就地排序的,将它们的合并不需要操作:整个数组A【p..r】已排序。

 

下面过程实现快速排序: 

【算法导论】排序---快速排序

 

数组划分:Partition(关键,它对子数组A【p..r】进行就地重排)

【算法导论】排序---快速排序

 

C++实现:

 

//*  算法导论 第七章 快速排序 *//  
#include  
#include  
#include  
    
   
void Swap(int &a,int &b){ if(a!=b){a^=b;b^=a;a^=b;} }  //如果两个数相等,就不执行位运算交换。因为如果两数相等,结果会是0  
   
   
int Partition(int *A,int p,int r){  
    int x, i;  
    x=A[r];  
    i=p-1;  
    for(int j=p; j<=r-1; ++j){  
        if(A[j]<=x) {  
            Swap(A[++i], A[j]);      
        }  
    }  
    Swap(A[++i],A[r]);  
    return i;  
}  
   
void QuickSort(int *A,int p,int r){  
    if(p

  

快排的性能分析:

当数据量很小的时候,大概就十来个元素的小型序列,快排的优势并不明显,甚至比插入排序慢。但是一旦数据多,它的优势就充分发挥出来了。

 

举一个例子,C++ STL 中的sort函数,就充分发挥了快排的优势,并且取长补短,在数据量大时采用QuickSort,分段递归排序。一旦分段后的数据量小于某个门槛,为避免QuickSort递归调用带来过大的额外负荷,就改用插入排序。如果递归层次过深,还会改用HeapSort(堆排序)。所以说,C++的“混合兵种”sort的性能肯定会比C的qsort好。

 

快排的运行时间与Partition的划分有关

最坏情况是输入的数组已经完全排好序,那么每次划分的左、右两个区域分别为n-1和0,效率为O( n^2 ).

而对于其他常数比例划分,哪怕是左右按9:1的比例划分,效果都是和在正中间划分一样快的(算法导论上有详细分析)

即,任何一种按照常数比例进行划分,总运行时间都是O(n lg n).

 

快排的随机化版本:

 因为快排中Partition所产生的划分中可能会有”差的“,而划分的关键在于主元A【r】的选择。我们可以采用一种不同的、称为随机取样的随机化技术,把主元A【r】和A【p..r】中随机选出一个元素交换,这样相当于,我们的主元不在是固定是最后一个A【r】,而是随机从p,...,r这一范围随机取样。 这样可以使得期望平均情况下,Partition的划分能够比较对称.

 

伪代码的实现 :

【算法导论】排序---快速排序

 

C++实现:

int Random(int m,int n){    
    srand((unsigned)time(NULL));  // 包含头文件  
    return m+(rand()%(n-m+1));  
}  
   
int Random_Partition(int *A,int p,int r){  
    int i=Random(p,r);  
    Swap(A[r],A[i]);  
    return Partition(A,p,r);  
}  
   
void Random_QuickSort(int *A,int p,int r){  
    if(p

 

快排的进一步优化的讨论:

 1、尾递归:

 传统的递归算法在很多时候被视为洪水猛兽. 它的名声狼籍, 好像永远和低效联系在一起,尾递归是极其重要的,不用尾递归,函数的堆栈耗用难以估量,需要保存很多中间函数的堆栈。

快排中的堆栈深度:

QUICKSORT算法包含两个对其自身的递归调用,即调用PARTITION后,左边的子数组和右边的子数组分别被递归排序。QUICKSORT中的第二次递归调用并不是必须的,可以用迭代控制结构来代替它,这种技术叫做“尾递归”,大多数的编译器也使用了这项技术

下面这个版本模拟了尾递归:

QUICKSORT'(A, p, r)

1  while p 

 

需要注意第一行是 while而不是if

但是这个版本在最坏的情况下,就是划分不好的时候,递归深度为O(n),可以再进一步优化使栈深度为O(lg n)吗?

 

用二分的思想,为了使最坏情况下栈的深度为Θ(lgn),我们必须是PARTITION后左边的子数组为原来数组的一半大小,这样递归的深度最多为Θ(lgn)。

一种可能的算法是:首先求得(A, p, r)的中位数,作为PARTITION的枢轴元素,这样可以保证左右两边的元素的个数尽可能的均衡。

因为求中位数的过程MEDIAN的时间复杂度为Θ(n),因此可以保证算法的期望的时间复杂度O(nlgn)不变。

 

优化版的尾递归快排:

【算法导论】排序---快速排序

 

2. "三数取中"划分

所谓“三数取中”是指,从子数组中随机选出三个元素,取其中间数作为主元,这算是前面随机化版本的升级版。虽然是升级版,但是也只能影响快速排序时间复杂度O(nlgn)的常数因子.

 

下面将给出综合了”优化的尾递归“+”三数取中“版本的Final 快排版本:

//  优化的尾递归 + 三数取中 版本快排   
#include  
#include  
#include  
    
   
void Swap(int &a,int &b){ if(a!=b){a^=b;b^=a;a^=b;} }  //如果两个数相等,就不执行位运算交换。因为如果两数相等,结果会是0  
   
   
int Partition(int *A,int p,int r){  
    int x, i;  
    x=A[r];  
    i=p-1;  
    for(int j=p; j<=r-1; ++j){  
        if(A[j]<=x) {  
            Swap(A[++i], A[j]);      
        }  
    }  
    Swap(A[++i],A[r]);  
    return i;  
}  
  
inline int Random(int m,int n){  
    srand((unsigned)time(NULL));  
    return m+(rand()%(n-m+1));  
}  
  
// 取出三个数的中间数(第二大的数)的函数  
inline int MidNum(int a,int b,int c){  
    if(c

  

 

除此之外,快排还可以有优化:

非递归的方法:即模拟递归,这样可以完全消去递归的调用。

三划分快速排序:基本思想是,在划分阶段以V=A[r]为基准,将带排序数组A【p..r】划分为左、中、右三段A【p,j】,

A【j+1..q-1】,A【q..r】,其中左段数组元素值小于V,中断数组等于V,有段数组元素大于V。其后,算法对左右

两段数组递归排序。 这个方法对于有大量相同数据的数组排序效率有很大的提高,即使没有大量相同元素,也不

降低原快排算法的效率。

 

以上两种以后有机会再把代码实现一遍吧。


推荐阅读
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
  • Java中包装类的设计原因以及操作方法
    本文主要介绍了Java中设计包装类的原因以及操作方法。在Java中,除了对象类型,还有八大基本类型,为了将基本类型转换成对象,Java引入了包装类。文章通过介绍包装类的定义和实现,解答了为什么需要包装类的问题,并提供了简单易用的操作方法。通过本文的学习,读者可以更好地理解和应用Java中的包装类。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 图像因存在错误而无法显示 ... [详细]
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 关于如何快速定义自己的数据集,可以参考我的前一篇文章PyTorch中快速加载自定义数据(入门)_晨曦473的博客-CSDN博客刚开始学习P ... [详细]
author-avatar
manassatromble
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有