学过编程的人都学过排序算法,学过排序算法的人肯定都听说过归并排序。
Timsort排序算法就是归并排序的改进算法。
回想下归并排序的特点:
- 排序过程需要将原数列一分为二,先将子序列排序好后在归并。整个排序过程接近于一颗完全二叉树。
- 它的比较次数相对稳定。如果对于已经有序(顺序或逆序)的结构依然花费比较比较高的时间。
Timsort就是对归并排序存在的缺点进行了改进。
归并排序其中之一的缺点就是它无法利用原始数列中已经排序好的数据段。这一点在Timsort中有改善。
首先Timsort会对数列进行分段,最开始分段的依据就是连续的递增或递减的序列。
这样做的原因是归并排序本身就是把已经排好序的两条子序列合成一条父序列。但是归并排序最开始的粒度是1,因为长度为1 的序列必然有序。而Timsort在一开始直接利用原序列本身存在的有序性。单调递增的,或单调递减的(单调递减翻转后就是单调递增的了)。
Timsort是稳定排序,它每次都是合并相邻的两条序列,同归并排序。(存储后的序列需要内存空间存放,合并相邻的序列很方便)。
由于归并排序的整个过程类似于一颗满二叉树的先序遍历,它是根据长度来划分的,越靠近跟结点长度越长,兄弟结点的长度差小于等于1。因为每次合并两条序列接近,整体的效率较高,它合并的复杂度是两条子序列的长度之和(可以想象哈夫曼树的构造)。在Timsort中我们也需要尽力去维持这样的方式。
粒度一般是维持在64。
如果小于64,则按照二分查找插入排序来进行数组排序。
如果大于64, Timsort 选择范围为 [32,64]的 minrun,使得原始数组的长度除以 minrun 等于或略小于2的幂。需要取数组长度的前6位数,保证>32且<64。如果原始粒度中,小于minrun的段,则从这个段的后面接连的一个数取来通过二分插入到这个小于minrun的段中。
完了以后再进行合并操作。
合并操作也需要一个规则来进行约束。
- X > Y + Z
- Y > Z
X、Y、Z代表栈最上方的3个run的长度,如果违反了上面的两条规则,则Y与X、Z中的较小者合并。