转载自:http://blog.csdn.net/yangzhongblog/article/details/8184707

Timsort是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法,它在现实中有很好的效率。Tim Peters在2002年设计了该算法并在Python中使用(TimSort 是 Python 中 list.sort 的默认实现)。该算法找到数据中已经排好序的块-分区,每一个分区叫一个run,然后按规则合并这些run。Pyhton自从2.3版以来一直采用Timsort算法排序,现在Java SE7和Android也采用Timsort算法对数组排序。

内容

1 操作

1.1 run的最小长度

1.2 优化run的长度

1.3 合并run

1.4 合并步骤

1.5 Galloping模型

2 性能

Timsort的核心过程

TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些 run 序列,每次拿一个 run 出来按规则进行合并。每次合并会将两个 run合并成一个 run。合并的结果保存到栈中。合并直到消耗掉所有的 run,这时将栈上剩余的 run合并到只剩一个 run 为止。这时这个仅剩的 run 便是排好序的结果。

综上述过程,Timsort算法的过程包括

(0)如何数组长度小于某个值,直接用二分插入排序算法

(1)找到各个run,并入栈

(2)按规则合并run

1 操作

现实中的大多数据通常是有部分已经排好序的,Timsort利用了这一特点。Timsort排序的输入的单位不是一个个单独的数字,而是一个个的分区。其中每一个分区叫一个“run“(图1)。针对这个 run 序列,每次拿一个 run 出来进行归并。每次归并会将两个 run 合并成一个 run。每个run最少要有2个元素。Timesor按照升序和降序划分出各个run:run如果是是升序的,那么run中的后一元素要大于或等于前一元素(a[lo] <= a[lo + 1] <= a[lo + 2] <= ...);如果run是严格降序的,即run中的前一元素大于后一元素(a[lo] >  a[lo + 1] >  a[lo + 2] >  ...),需要将run 中的元素翻转(这里注意降序的部分必须是“严格”降序才能进行翻转。因为 TimSort 的一个重要目标是保持稳定性stability。如果在 >= 的情况下进行翻转这个算法就不再是 stable)。

1.1 run的最小长度

run是已经排好序的一块分区。run可能会有不同的长度,Timesort根据run的长度来选择排序的策略。例如如果run的长度小于某一个值,则会选择插入排序算法来排序。run的最小长度(minrun)取决于数组的大小。当数组元素少于64个时,那么run的最小长度便是数组的长度,这是Timsort用插入排序算法来排序。当数组元素大于等于63时,For larger arrays, a number, referred to as minrun, is chosen from the range 32 to 65, such that the size of the array, divided by the minimum run size, is equal to, or slightly smaller than, a power of two. The final algorithm for this simply takes the six most significant bits of the size of the array, adds one if any of the remaining bits are set, and uses that result as the minrun. This algorithm works for all cases, including the one in which the size of the array is smaller than 64.

 

图1 run

1.2  优化run的长度

优化run的长度是指当run的长度小于minrun时,为了使这样的run的长度达到minrun的长度,会从数组中选择合适的元素插入run中。这样做使大部分的run的长度达到均衡,有助于后面run的合并操作。

1.3 合并run

划分run和优化run长度以后,然后就是对各个run进行合并。合并run的原则是 run合并的技术要保证有最高的效率。当Timsort算法找到一个run时,会将该run在数组中的起始位置和run的长度放入栈中,然后根据先前放入栈中的run决定是否该合并run。Timsort不会合并在栈中不连续的run(Timsort does not merge non-consecutive runs because doing this would cause the element common to all three runs to become out of order with respect to the middle run.)

Timsort会合并在栈中2个连续的run。X、Y、Z代表栈最上方的3个run的长度(图2),当同时不满足下面2个条件是,X、Y这两个run会被合并,直到同时满足下面2个条件,则合并结束:

(1) X>Y+Z

(2) Y>Z

例如:如果X<Y+Z,那么X+Y合并为一个新的run,然后入栈。重复上述步骤,直到同时满足上述2个条件。当合并结束后,Timsort会继续找下一run,然后找到以后入栈,重复上述步骤,及每次run入栈都会检查是否需要合并2个run。

 

图2 合并run

1.4 合并run步骤

合并2个相邻的run需要临时存储空闲,临时存储空间的大小是2个run中较小的run的大小。Timsort算法先将较小的run复制到这个临时存储空间,然后用原先存储这2个run的空间来存储合并后的run(图3)。

 

图3 临时存储空间

简单的合并算法是用简单插入算法,依次从左到右或从右到左比较,然后合并2个run。为了提高效率,Timsort用二分插入算法(binary merge sort)。先用二分查找算法/折半查找算法(binary search)找到插入的位置,然后在插入。
         例如,我们要将A和B这2个run 合并,且A是较小的run。因为A和B已经分别是排好序的,二分查找会找到B的第一个元素在A中何处插入(图4)。同样,A的最后一个元素找到在B的何处插入,找到以后,B在这个元素之后的元素就不需要比较了(图5)。这种查找可能在随机数中效率不会很高,但是在其他情况下有很高的效率。

 

图4 run合并过程1

 

图5 run合并过程2

1.5 Galloping 模型

介绍的是类似上述run的合并过程,参见维基百科 Galloping Model

2 性能

根据信息学理论,在平均情况下,比较排序不会比O(n log n)更快。由于Timsort算法利用了现实中大多数数据中会有一些排好序的区,所以Timsort会比
O(n log n)快些。对于随机数没有可以利用的排好序的区,Timsort时间复杂度会是log(n!)。下表是Timsort与其他比较排序算法时间复杂度(time complexity)的比较。

 

空间复杂度(space complexities)比较

 

说明:

JSE 7对对象进行排序,没有采用快速排序,是因为快速排序是不稳定的,而Timsort是稳定的。

下面是JSE7 中Timsort实现代码中的一段话,可以很好的说明Timsort的优势:

A stable, adaptive, iterative mergesort that requires far fewer than n lg(n) comparisons when running on partially sorted arrays, while offering performance comparable to a traditional mergesort when run on random arrays.  Like all proper mergesorts, this sort is stable and runs O(n log n) time (worst case).  In the worst case, this sort requires temporary storage space for n/2 object  references; in the best case,  it requires only a small constant amount of space.

大体是说,Timsort是稳定的算法,当待排序的数组中已经有排序好的数,它的时间复杂度会小于n logn。与其他合并排序一样,Timesrot是稳定的排序算法,最坏时间复杂度是O(n log n)。在最坏情况下,Timsort算法需要的临时空间是n/2,在最好情况下,它只需要一个很小的临时存储空间

Timsort 算法的更多相关文章

  1. Java TimSort算法 源码 笔记

    本来准备看Java容器源码的.但是看到一开始发现Arrays这个类我不是很熟,就顺便把Arrays这个类给看了.Arrays类没有什么架构与难点,但Arrays涉及到的两个排序算法似乎很有意思.那顺便 ...

  2. 形式化方法的逆袭——如何找出Timsort算法和玉兔月球车中的Bug?

    https://bindog.github.io/blog/2015/03/30/use-formal-method-to-find-the-bug-in-timsort-and-lunar-rove ...

  3. 简易版的TimSort排序算法

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 简易版本TimSort排序算法原理与实现 TimSort排序算法是Python和Ja ...

  4. CompareTo 基于的排序算法

    CompareTo 基于的排序算法(高级排序) 这个是今天学习MapReduce时发现的,自定义类后实现了WritableComparable<>接口后实现了接口中的compareTo方法 ...

  5. JDK(二)JDK1.8源码分析【排序】timsort

    如无特殊说明,文中的代码均是JDK 1.8版本. 在JDK集合框架中描述过,JDK存储一组Object的集合框架是Collection.而针对Collection框架的一组操作集合体是Collecti ...

  6. TimSort学习资料

    深入理解 timsort 算法(1):自适应归并排序 如何找出Timsort算法和玉兔月球车中的Bug? Java TimSort算法 源码 笔记 Timsort https://en.wikiped ...

  7. 浅入浅出 Java 排序算法

    Java String 源码的排序算法 一.前言 Q:什么是选择问题? 选择问题,是假设一组 N 个数,要确定其中第 K 个最大值者.比如 A 与 B 对象需要哪个更大?又比如:要考虑从一些数组中找出 ...

  8. TimSort源码详解

    Python的排序算法由Peter Tim提出,因此称为TimSort.它最先被使用于Python语言,后被多种语言作为默认的排序算法.TimSort实际上可以看作是mergeSort+binaryS ...

  9. Arrays.Sort()中的那些排序算法

    本文基于JDK 1.8.0_211撰写,基于java.util.Arrays.sort()方法浅谈目前Java所用到的排序算法,仅个人见解和笔记,若有问题欢迎指证,着重介绍其中的TimSort排序,其 ...

随机推荐

  1. linux下判断文件和目录是否存在

    1.前言 工作中涉及到文件系统,有时候需要判断文件和目录是否存在.我结合APUE第四章文件和目录,总结一下如何正确判断文件和目录是否存在,方便以后查询. 2.stat系列函数 stat函数用来返回与文 ...

  2. php json的相关操作

    类与json <?php class userinfo { var $user_name; var $user_ip; var $user_logintime; } $u = new useri ...

  3. Mixing ASP.NET Webforms and ASP.NET MVC

    https://www.packtpub.com/books/content/mixing-aspnet-webforms-and-aspnet-mvc *********************** ...

  4. Hadoop集群datanode死掉或者secondarynamenode进程消失处理办法

    当Hadoop集群的某单个节点出现问题时,一般不必重启整个系统,只须重启这个节点,它会自动连入整个集群. 在坏死的节点上输入如下命令即可: hadoop-daemon.sh start datanod ...

  5. RTX——第13章 事件标志组

    以下内容转载自安富莱电子: http://forum.armfly.com/forum.php 前面的章节我们已经讲解了任务管理和时间管理,从本章节开始讲解任务间的通信和同步机制.首先讲解任务间的通信 ...

  6. HTML5 Canvas 梦幻的文字飞扬动画教程

    之前为大家介绍了一款HTML5/CSS3 3D文字特效 文字外翻效果.今天为大家带来的是html5 canvas实现的文字漂动动画效果.画面非常梦幻. 在线预览   源码下载 实现代码如下: html ...

  7. 一款纯css3实现的颜色渐变按钮

    之前为大家分享了推荐10款纯css3实现的实用按钮,今天给大家带来一款纯css3实现的颜色渐变按钮.这款按钮的边框和文字的颜色通过css3实现两种颜色的渐变,效果非常好看,一起看下效果图: 在线预览  ...

  8. php和js以及ts的传值

    egret是用ts写h5游戏,然后编译为js,有些数据想在加载时由服务端生成. 一种方法用网络请求,另外一种方法直接传值. 网络请求如果在运行中,有数据交互,其实是个比较好的选择. 而我只是在初始化时 ...

  9. mysql自增id获取失败

    php 数据库pdo对象,如果是返回,如以下伪代码 function getData(){ return $data; } $data = getData(); $id = $data->las ...

  10. SVN导入maven项目

    在项目中,曾今遇到过这种问题,用eclipse将项目从svn下载下来,maven去自动下载jar包怎么都报错,本来时间就很紧张, 还特么遇到这种坑爹的问题.不过,整了我一天,最后终于在同事的帮助下,搞 ...