【Java】Arrays.sort:TimSort
一,概述
书接前文【Java】Arrays.sort:DualPivotQuicksort-CSDN博客
Arrays.sort对基本数据类型使用了双轴快速排序,但是对Object[]类型,则使用了TimSort,TimSort是稳定的排序,它整合了插入排序+归并排序,存在空间换时间策略,本文简单解析下此算法实现。
二,实现
入口,Object[]调用Arrays.sort方法
sort内部根据配置策略,使用旧版归并排序,或TimSort,
进入第二个分支,以下sort算法即TimSort,本篇文章分析核心。
TimSort是插入排序+归并排序结合的算法,兼顾了两种排序的优点,比如插入排序时间复杂度最优可以达到O(N),但最差O(N^2)。归并排序是稳定排序,时间复杂度是O(NLogN),空间复杂度O(N),TimSort结合了两种排序优缺点,时间复杂度在O(N)~O(NLogN),空间复杂度是O(N)。
此sort算法分步骤进行如下:
1,计算序列最小分块长度
2,对序列分块进行线性扫描,得到N块有序序列
3,对有序序列进行合并
4,当有序序列最后只剩1块,排序完成,
接下来,看下java内部实现
1,拿到序列size,保存至nRemaining,表示剩余序列长度
2,如果序列长度小于最小分块MIN_MERGE(32),则直接使用插入排序,此处插入排序用的binarySort,算是对查询优化,即插入新item时,会通过二分查找算法快速找到插入位置,这里就简单理解为插入排序即可。
当nRemaining >= MIN_MERGE时,开始计算最小分块个数
minRunLength是返回最小分块长度,由以上注释知,
minRunLength = n (n < MIN_MERGE) or
MIN_MERGE/ 2 <= minRunLength <= MIN_MERGE,使得得到的序列个数n/k接近2的幂数,
此处不赘述
下一步,对最小分块进行排序,
1,线性扫描一遍,简单将指针经过序列排序,
2,如果得到的runLen长度(已有序块)< 最小分块长度,则调用插入排序重新排序,否则就忽略,
这个runLen可能是1,也可能是整个序列长度(序列原本有序),经过以上步骤,会将原序列分成N个有序序列,
下一步,将扫描有序序列入栈,并且合并,如下
栈使用数组记录,保存了每个分块的起点位置和长度,
mergeCollapse可能对stack中分组进行合并,合并算法不赘述,
最后,当整个序列合并完成后,stack中只存在一个序列,done