zoukankan      html  css  js  c++  java
  • 数据结构与算法学习笔记之编程语言中的排序函数是怎么实现的

    前言

       在开发过程中,人人都会用到排序,每种编程语言也会提供排序函数,可是编程语言的排序函数运用环境复杂,必须得达到最大程度的兼容。我们得怎么实现一种通用的,高效的排序函数呢?

    正文

     1.最通用的排序算法 

    如下图所示:

    在我们选取的排序函数中,O(n2)时间复杂度适合小规模的排序,O(nlogn)时间复杂度适合大规模的排序,为了兼顾任意规模,我们选取时间复杂为O(nlogn)的算法,如:归并,快排,堆排序。

      归并排序,时间复杂度符合要求,可是他不属于原地排序算法,空间复杂度太大,排序1G的数据就需要占用2G的空间

      2.怎么优化快速排序?

      快速排序的时间复杂度为O(nlogn),但是在最坏的情况时,我们每次选取的分区结点都选择最后一个数据时,时间复杂都会变为0(n2),那么最好的分区点就是:被分区点分开的两个分区中,数据数量差不多。

    刚好有两种简单常用的分区算法来优化:

    1.三数取中法

      我们从区间的首尾中,分别取出一个数据,然后对比大小,取中间值作为分区点。

     但是这里有个弊端:当数据规模太大时,三数取中显然就不够了,需要五数取中,甚至十数取中法。

    2.随机发

      每次从区间中,选取一个元素作为分区点,这种方法并不能保证每次分区点都非常好,但是从概率学的角度来看,不太可能会出现每次分区点都选的很差的情况,所以这样分区是比较好的。

    还有很多的分区算法这里就不重点叙述了。有兴趣的,自己学习一下

      3.Java的Sort函数

      在各大编程语言中都提供了排序函数,在Java中的sort你阅读源码会发现,  

    在JDK1.7版本中Arrays.sort()方法是根据传出参数的长度的大小来判断用哪种排序方法,一种是针对基本类型的数据,主要是有归并排序、快速排序、插入排序、计数排序,而另一种则是针对对象类型的排序,改进的归并排序--合并排序

       合并排序是稳定的并且合并排序比较的次数比快排,合并排序的时间复杂度是n*logn, 快速排序的平均时间复杂度也是n*logn,但是合并排序的需要额外的n个引用的空间。排序100M的数据,需要额外100M的空间。

    函数中关于阈值的源码如下:

     /**
         * The maximum number of runs in merge sort.
        合并排序的最大运行次数。
         */
        private static final int MAX_RUN_COUNT = 67;
    
        /**
         * The maximum length of run in merge sort.
         * 归并排序中的最大值,归并排序是稳定的,时间复 杂度为O(nlogn)
         */
        private static final int MAX_RUN_LENGTH = 33;
    
        /**
         * If the length of an array to be sorted is less than this
         * constant, Quicksort is used in preference to merge sort.
         * 快速排序中的最大值,快速排序是不稳定的,时间复杂度为O(nlogn),最坏情况下是O(n^2)
         */
        private static final int QUICKSORT_THRESHOLD = 286;
    
        /**
         * If the length of an array to be sorted is less than this
         * constant, insertion sort is used in preference to Quicksort.
            如果要排序的数组的长度小于此值
              常量,插入排序优先于Quicksort使用。
         */
        private static final int INSERTION_SORT_THRESHOLD = 47;
    
        /**
         * If the length of a byte array to be sorted is greater than this
         * constant, counting sort is used in       preference to insertion sort.
            如果要排序的字节数组的长度大于此值
           常量,计数排序优先于插入排序。
         */
        private static final int COUNTING_SORT_THRESHOLD_FOR_BYTE = 29;
    
        /**
         * If the length of a short or char array to be sorted is greater
         * than this constant, counting sort is used in preference to Quicksort.
            如果要排序的short或char数组的长度更大
            比这个常量,计数排序优先于Quicksort使用。
         */
        private static final int COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR = 3200;
                

    当排序的数据规模较少时:插入排序优于快速排序

    在快排时,其中有一段是:当数组大小 7<size<=40时,取首、中、末三个元素中间大小的元素作为划分元。采用的优化方案也是三数取中法。

    具体的大家可以阅读源码;

    4、c语言中qsort()函数

    它是一种基于快速排序,归并排序,插入排序的排序函数

    当排序的数据规模很小时,如1k、2k这种我们都是用的归并排序,虽然归并排序需要额外的空间,但是这些小规模的数据用递归是速度最快的,而且空间消耗我们也负担得起,这里就很好的使用空间交换时间的技巧。

    当排序的数据规模很大时,源码中采用的就是优化过的快排,而且优化的方法就是“三数取中法”。在使用快排中,当排序的区间中,元素的个数小于等于4时,qsort()就会退化为插入排序,不再使用递归来做快速排序,小规模数据面前O(n2)时间复杂度并不一定比O(nlogn)执行时间长。

    下图为y=n2的函数

     下图网上寻找到的y=nlogn与y=n2在一张图里的图片。你会发现O(n2) 比 O(nlogn) 增涨趋势更猛烈,所以对于小数据量的排序,我们使用比较简单,不需要递归的插入排序

    相关文章

    数据结构与算法学习笔记之写链表代码的正确姿势(下)

    数据结构与算法学习笔记之 提高读取性能的链表(上)

    数据结构与算法学习笔记之 从0编号的数组

    数据结构与算法学习笔记之后进先出的“桶”

    数据结构与算法学习笔记之先进先出的队列

    数据结构与算法学习笔记之高效、简洁的编码技巧“递归”

    数据结构与算法学习笔记之如何分析一个排序算法?

    数据结构与算法学习笔记之 适合大规模的数据排序

    数据结构与算法学习笔记之为用于高考名次排

    以上内容为个人的学习笔记,仅作为学习交流之用。

     

    欢迎大家关注公众号,不定时干货,只做有价值的输出

    作者:Dawnzhang 
    出处:https://www.cnblogs.com/clwydjgs/

    小舟从此逝,江海寄余生。 --狐狸
  • 相关阅读:
    C#的默认访问权限
    隐藏基类成员
    索引指示器的重载
    索引指示器
    vector
    string 函数
    细胞个数
    计蒜客 T1096 石头剪刀布
    计蒜客 T1125 判断字符串是否为回文
    计蒜客 T1152 成绩排序
  • 原文地址:https://www.cnblogs.com/clwydjgs/p/9949854.html
Copyright © 2011-2022 走看看