TimSort--归并排序的

简介

MergeSort对已经反向排好序的输入时复杂度为O(n^2),而timsort就是针对这种情况,对MergeSort进行优化而产生的,平均复杂度为n*O(log n),最好的情况为O(n),最坏情况n*O(log n)。并且TimSort是一种稳定性排序。思想是先对待排序列进行分区,然后再对分区进行合并,看起来和MergeSort步骤一样,但是其中有一些针对反向和大规模数据的优化处理。

步骤

分区

分区的思想是扫描一次数组,把连续正序列(如果是升序排序,那么正序列就是升序序列),或者【严格】(保证排序算法的稳定性)的反序列做为一个分区(run),如果是反序列,把分区里的元素反转一下。 例如

1,2,3,6,4,5,8,6,4 划分分区结果为

[1,2,3,6],[4,5,8],[6,4]

然后反转反序列

[1,2,3,6],[4,5,8],[4,6]

合并

考虑一个极端的例子,比如分区的长度分别为 10000,10,1000,10,10,我们当然希望是先让10个10合并成20, 20和1000合并成1020如此下去, 如果从从左往右顺序合并的话,每次都用到10000这个数组和去小的数组合并,代价太大了。所以我们可以用一个策略来优化合并的顺序。

实例

以java中的ComparableTimSort.sort()为例子, 用了一个run stack来确定是否应该合并,


        if (nRemaining < MIN_MERGE) {
            int initRunLen = countRunAndMakeAscending(a, lo, hi);
            binarySort(a, lo, hi, lo + initRunLen);
            return;
        }

小于MIN_MERGE(32)的排序,分区后直接用二分插入排序


int minRun = minRunLength(nRemaining);
        do {
            //找出下一个分区的起始位置,同时也对反向序列做了翻转处理
            int runLen = countRunAndMakeAscending(a, lo, hi);

            //保证run stack中的run的都大于minRun ,如果当前分区太小,就从后面取出元素补足
            if (runLen < minRun) {
                int force = nRemaining <= minRun ? nRemaining : minRun;
                binarySort(a, lo, lo + force, lo + runLen);
                runLen = force;
            }

            //把run放入 run stack中
            ts.pushRun(lo, runLen);
            //判断是否应该合并,i是从栈顶开始的,知道不能合并为止
            //1. runLen[i - 3] > runLen[i - 2] + runLen[i - 1]
            //2. runLen[i - 2] > runLen[i - 1]
            ts.mergeCollapse();

            lo += runLen;
            nRemaining -= runLen;
        } while (nRemaining != 0);

        // Merge all remaining runs to complete sort
        assert lo == hi;
        //合并剩下的run
        ts.mergeForceCollapse();
        assert ts.stackSize == 1;

在看里面的一个比较重要的函数

/**
* 如果后2个run的长度加起来比前面一个长,则使用中间位置的run和前后长度更短的run一个合并
* 如果后2个run的长度加起来比前面一个短,则把后面2个run合并
*/
 private void mergeCollapse() {
        while (stackSize > 1) {
            int n = stackSize - 2;
            if (n > 0 && runLen[n-1] <= runLen[n] + runLen[n+1]) {
                if (runLen[n - 1] < runLen[n + 1])
                    n--;
                mergeAt(n);
            } else if (runLen[n] <= runLen[n + 1]) {
                mergeAt(n);
            } else {
                break; // Invariant is established
            }
        }
    }
时间: 2024-11-03 06:21:31

TimSort--归并排序的的相关文章

自顶向下归并排序和自底向上的归并排序

欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 归并排序算法的使用情景 归并排序算法和快速排序算法是java.util.Arrays中使用的排序算.对于一般的基本数据类型,Arrays.sort函数使用双轴快速排序算法,而对于对象类型使用归并排序(准确的说使用的是TimSort排序算法,它是归并排序的优化版本).这样做的原因有两点,第一个原因,归并排序是稳定的,而快速排序不是稳定的.第二个原因,对于基本数据类型,排序的稳定性意义

OpenJDK 源码阅读之 TimSort

概要 这个类在 Oracle 的官方文档里是查不到的,但是确实在 OpenJDK 的源代码里出现了,Arrays 中的 sort 函数用到了这个用于排序的类.它将归并排序(merge sort) 与插入排序(insertion sort) 结合,并进行了一些优化.对于已经部分排序的数组,时间复杂度远低于 O(n log(n)),最好可达 O(n),对于随机排序的数组,时间复杂度为 O(nlog(n)),平均时间复杂度 O(nlog(n)).强烈建议在看此文前观看 Youtube 上的 可视化Ti

简易版的TimSort排序算法

欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 简易版本TimSort排序算法原理与实现 TimSort排序算法是Python和Java针对对象数组的默认排序算法.TimSort排序算法的本质是归并排序算法,只是在归并排序算法上进行了大量的优化.对于日常生活中我们需要排序的数据通常不是完全随机的,而是部分有序的,或者部分逆序的,所以TimSort充分利用已有序的部分进行归并排序.现在我们提供一个简易版本TimSort排序算法,它

TimSort算法分析

Timsort是一种混合稳定的排序算法,采用归并排序混合插入排序的设计,在多种真实数据上表现良好. 它基于一个简单的事实,实际中大部分数据都是部分有序(升序或降序)的. 它于2002年由Tim Peters在Python编程语言实现. Timsort排序算法中定义数组中的有序片段为run,每个run都要求单调递增或严格单调递减(保证算法的稳定性),如下图: 文中图片都是我手绘的,字写的难看了点,将就看吧~ Timsort排序算法可以概括成两步: 1. 把待排序数组分成一个个的run(即单调上升的

TimSort排序算法及一个问题分析

摘要 排序算法简析 代码入口 排序算法 获取两个有序数组A和B 找到待归并区间 准备操作 归并操作 TimSort的优化归并操作 问题解析 问题解析 问题原因 解决方案 参考 摘要 简单介绍了传统归并排序算法,以及Java API提供的TimSort优化后的归并排序算法. 并且分析了代码中出现的一个问题原因与解决方案. 敬请忽略文中的灵魂画风. 排序算法简析 代码入口 Collections.sort.List.sort.Arrays.sort方法是逐级调用的关系,最终的底层是Arrays.so

图解排序算法(四)之归并排序

基本思想 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之). 分而治之 可以看到这种结构很像一棵完全二叉树,本文的归并排序我们采用递归去实现(也可采用迭代的方式去实现).分阶段可以理解为就是递归拆分子序列的过程,递归深度为log2n. 合并相邻有序子序列 再来看看治阶段

排序(三)归并排序

参考文档 https://www.cnblogs.com/chengxiao/p/6194356.html 原理: 归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之).即把原始数组分成若干子数组,对每一个子数组进行排序,继续把子数组与子数组合并,合并后仍然有序,直到全部合

排序问题之归并排序

排序问题 算法问题的基础问题之一,便是排序问题: 输入:n个数的一个序列,<a1, a2,..., an>. 输出:一个排列<a1',a2', ... , an'>,满足a1' ≤ a2' ≤... ≤ an' .(输出亦可为降序,左边给出的例子为升序) 一.算法描述 (1)分治法 归并排序是使用到了分治方法(Divide and Conquer). Divide:将原问题分解为若干子问题,其中这些子问题的规模小于原问题的规模. Conquer:递归地求解子问题,当子问题规模足够小

算法 排序NB二人组 堆排序 归并排序

参考博客:基于python的七种经典排序算法     常用排序算法总结(一) 序前传 - 树与二叉树 树是一种很常见的非线性的数据结构,称为树形结构,简称树.所谓数据结构就是一组数据的集合连同它们的储存关系和对它们的操作方法.树形结构就像自然界的一颗树的构造一样,有一个根和若干个树枝和树叶.根或主干是第一层的,从主干长出的分枝是第二层的,一层一层直到最后,末端的没有分支的结点叫做叶子,所以树形结构是一个层次结构.在<数据结构>中,则用人类的血统关系来命名,一个结点的分枝叫做该结点的"

codeforces 414C C. Mashmokh and Reverse Operation(归并排序求逆序对)

题目链接: C. Mashmokh and Reverse Operation time limit per test 4 seconds memory limit per test 512 megabytes input standard input output standard output Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university