Java怎么实现排序算法Timsort


这篇文章主要介绍“Java怎么实现排序算法Timsort”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java怎么实现排序算法Timsort”文章能帮助大家解决问题。Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序二分插入排序算法的混合体,号称世界上最好的排序算法。Timsort一直是 Python 的标准排序算法。Java SE 7 后添加了Timsort API ,我们从Arrays.sort可以看出它已经是非原始类型数组的默认排序算法了。所以不管是进阶编程学习还是面试,理解 Timsort 是比较重要。理解 Timsort 需要先回顾下面的知识。指数搜索,也被称为加倍搜索,是一种用于在大型数组中搜索元素而创建的算法。它是一个两步走的过程。首先,该算法试图找到目标元素存在的范围(L,R),然后在这个范围内使用二叉搜索来寻找目标的准确位置。时间复杂度为O(lgn)。该搜索算法在大量有序数组中比较有效。插入排序算法很简单,大体过程是从第二个元素开始,依次向前移动交换元素直到找到合适的位置。插入排序最优时间复杂度也要O(n),我们可以使用二分查找来减少插入时元素的比较次数,将时间复杂度降为logn。但是注意,二分查找插入排序仍然需要移动相同数量的元素,但是复制数组的时间消耗低于逐一互换操作。特点:二分插入排序主要优点是在小数据集场景下排序效率很高。归并排序是利用分治策略的算法,包含两个主要的操作:分割合并。大体过程是,通过递归将数组不断分成两半,一直到无法再分割(也就是数组为空或只剩一个元素),然后进行合并排序。简单来说合并操作就是不断取两个较小的排序数组然后将它们组合成一个更大的数组。特点:归并排序主要为大数据集场景设计的排序算法。算法大体过程,如果数组长度小于指定阀值(MIN_MERGE)直接使用二分插入算法完成排序,否则执行下面步骤:先从数组左边开始,执行升序运行得到一个子序列。将这个子序列放入运行堆栈里,等待执行合并。检查运行堆栈里的子序列,如果满足合并条件则执行合并。重复第一个步骤,执行下一个升序运行升序运行就是从数组查找一段连续递增(升序)或递减(降序)子序列的过程,如果子序列为降序则将它反转为升序,也可以将这个过程简称为run。比如数组 [2,3,6,4,9,30],可以查找到三个子序列,[2,3,6]、[4,9]、[30],或说3个runMIN_MERGE这是个常数值,可以简单理解为执行归并的最小阀值,如果整个数组长度小于它,就没必要执行那么复杂的排序,直接二分插入就行了。在 Tim Peter 的 C 实现中为 64,但实际经验中设置为 32 效果更好,所以 java 里面此值为 32。降序反转时为保证稳定性,相同元素不会被颠倒。minrun在合并序列的时候,如果run数量等于或者略小于 2 的幂次方的时候,合并效率最高;如果略大于 2 的幂次方,效率就会显著降低。所以为了提高合并效率,需要尽量控制每个run的长度,通过定义一个minrun来表示每个run的最小长度,如果长度太短,就用二分插入排序把run后面的元素插入到前面的run里面。一般在执行排序算法之前,会先计算免费云主机域名出这个 minrun(它是根据数据的特点来进行自我调整),minrun 会从32到64选择一个数字,因此数据的大小除以 minrun 等于或略小于 2 的幂次方。比如长度是 65 ,那么 minrun 的值就是 33;如果长度是 165,minrun 就是 42。看下 Java 里面的实现,如果数据长度(n) 数据长度恰好是 2 的幂次方,则返回MIN_MERGE/2
也就是16,否则返回一个MIN_MERGE/2
MIN_GALLOPMIN_GALLOP 是为了优化合并的过程设定的一个阈值,控制进入GALLOP模式中,GALLOP模式放在后面讲。下面是 Timsort 执行流程图当栈里面的run满足合并条件时,它就将栈里面相邻的两个run 进行合并。Timsort 为了执行平衡合并(让合并的 run 大小尽可能相同),制定了一个合并规则,对于在栈顶的三个run,分别用X、Y 和 Z 表示他们的长度,其中 X 在栈顶,它们必须始终维持一下的两个规则:一旦有其中的一个条件不被满足,则将 Y 与 X 或 Z 中的较小者合并生成新的run,并再次检查栈顶是否仍然满足条件。如果不满足则会继续进行合并,直至栈顶的三个元素都满足这两个条件,如果只剩两个run,则满足 Y > X 即可。如下下图例子当 Z 检测 Y 我们看下 Java 里面的合并实现原始归并排序空间复杂度是O(n)也就是数据大小。为了实现中间项,Timsort 进行了一次归并排序,时间开销和空间开销都比O(n)小。优化是为了尽可能减少数据移动,占用更少的临时内存,先找出需要移动的元素,然后将较小序列复制到临时内存,在按最终顺序排序并填充到组合序列中。比如我们需要合并 X [1, 2, 3, 6, 10] 和 Y [4, 5, 7, 9, 12, 14, 17],X 中最大元素是10,我们可以通过二分查找确定,它需要插入到 Y 的第 5个位置才能保证顺序,而 Y 中最小元素是4,它需要插入到 X 中的第4个位置才能保证顺序,那么就知道了[1, 2, 3] 和 [12, 14, 17] 不需要移动,我们只需要移动 [6, 10] 和 [4, 5, 7, 9],然后只需要分配一个大小为 2 临时存储就可以了。在归并排序算法中合并两个数组需要一一比较每个元素,为了优化合并的过程,设定了一个阈值 MIN_GALLOP,当B中元素向A合并时,如果A中连续 MIN_GALLOP 个元素比B中某一个元素要小,那么就进入GALLOP模式。根据基准测试,比如当A中连续7个以上元素比B中某一元素小时切入该模式效果才好,所以初始值为7。当进入GALLOP模式后,搜索算法变为指数搜索,分为两个步骤,比如想确定 A 中元素x在 B 中确定的位置首先在 B 中找到合适的索引区间(2k−1,2k+1−1)使得 x 元素在这个范围内;然后在第一步找到的范围内通过二分搜索来找到对应的位置。只有当一次运行的初始元素不是另一次运行的前七个元素之一时,驰骋才是有益的。这意味着初始阈值为 7。关于“Java怎么实现排序算法Timsort”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注百云主机行业资讯频道,小编每天都会为大家更新不同的知识点。

相关推荐: .Net Core怎么自定义日志组件

这篇文章主要介绍“.NetCore怎么自定义日志组件”,在日常操作中,相信很多人在.NetCore怎么自定义日志组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”.NetCore怎么自定义日志组件”的疑惑有所帮助!接下来,请跟着…

免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。

Like (0)
Donate 微信扫一扫 微信扫一扫
Previous 03/17 17:26
Next 03/17 17:26

相关推荐