当前位置: 首页 > 后端技术 > Python

世界上最快的排序算法——Timsort

时间:2023-03-26 19:41:12 Python

背景Timsort是一种混合稳定的排序算法。简单地说,它是归并排序和二进制插入排序算法的混合体。它被称为世界上最好的排序算法。Timsort长期以来一直是Python的标准排序算法。TimsortAPI是在JavaSE7之后加入的,我们从Arrays.sort中可以看出它已经是非原始数组的默认排序算法。所以无论是高级编程学习还是面试,了解Timsort都比较重要。//Listsort()defaultvoidsort(Comparatorc){Object[]a=this.toArray();//数组排序Arrays.sort(a,(Comparator)c);...}//Arrays.sortpublicstaticvoidsort(T[]a,Comparatorc){if(c==null){排序(a);}else{//弃用版本if(LegacyMergeSort.userRequested)legacyMergeSort(a,c);否则TimSort.sort(a,0,a.length,c,null,0,0);}}publicstaticvoidsort(Object[]a){if(LegacyMergeSort.userRequested)legacyMergeSort(a);否则ComparableTimSort.sort(a,0,a.length,null,0,0);}前置知识要理解Timsort,需要先复习一下以下知识。指数搜索指数搜索,也称为加倍搜索,是一种为搜索大型数组中的元素而创建的算法。这是一个两步过程。首先,该算法尝试找到目标元素存在的范围(L,R),然后在这个范围内使用二分查找找到目标的确切位置。时间复杂度为\(O(\lgn)\)。该搜索算法在大型有序数组中运行良好。二分插入排序插入排序算法非常简单。大致过程是从第二个元素开始,往前交换元素,直到找到合适的位置。插入排序的最优时间复杂度也是\(O(n)\),我们可以使用二分查找来减少插入时元素比较的次数,将时间复杂度降低到\(\logn\)。但是要注意,二分查找插入排序仍然需要移动相同数量的元素,但是复制数组的时间消耗比一对一的交换操作要低。特点:二分插入排序的主要优点是在小数据集场景下排序效率非常高。publicstaticint[]sort(int[]arr)throwsException{//开始遍历第一个元素之后的所有元素for(inti=1;i0&&tmp=end){return;}//待拆分数组的长度intlen=end-start;intmid=(len>>1)+开始;intleft=开始;//左子数组的起始索引intright=mid+1;//右子数组的起始索引//递归地切割左子数组直到只有一个元素mergeSortRecursive(arr,result,left,mid);//递归切右子数组直到只有一个元素mergeSortRecursive(arr,result,right,end);intk=开始;while(left<=mid&&right<=end){结果[k++]=arr[left]=0;整数r=0;//如果任何低位为1,它将变为1while(n>=MIN_MERGE){r|=(n&1);n>>=1;}返回n+r;}MIN_GALLOPMIN_GALLOP是为了优化合并过程而设置的阈值,控制进入GALLOP模式,后面会讲到。下面是Timsort执行流程图TBA[开始排序]A-->B{MIN_MERGE?}B-->|MIN_MERGE>=32|C[合并排序]B-->|MIN_MERGE<32|D[二进制排序]D-->E[升序运行]E-->F[执行排序]C-->G11[计算minRun]G11-->G1[升序运行]G1-->G2[获取子序列运行]G1-->|runLenid1G22-->G2G2-->|放入|ZZ-->|检查运行|Z1{合并条件}Z1-->|满足|Z2[执行合并]Z2-->|不满意|G3Z1-->|不满意|G3G3[准备下一次升序运算]G3-->|circulation|G1id1[\使用二分插入排序把runafter把上一次run的元素插入到\]Z[(runningstack)]runmerge时栈中的run满足合并条件,则合并栈中相邻的两个run。合并条件Timsort制定了一个合并规则,目的是为了进行平衡合并(使合并后的游程大小尽可能相同),对于栈顶的三个游程,用X、Y、Z来表示它们的长度,其中X在栈顶,他们必须始终保持以下两条规则:$$Z>Y+X\\Y>X$$一旦其中一个条件不满足,将Y与X或Z产生新的运行,再次检查栈顶是否还满足条件。如果不满足,则继续合并,直到栈顶的三个元素满足这两个条件。如果只剩下两次运行,则满足Y>X。如下例所示,当Z<=Y+X时,合并X+Y,只剩下两次run。检测到Y1){//表示Yintn=stackSize-2;//Z<=Y+Xif(n>0&&runLen[n-1]<=runLen[n]+runLen[n+1]){//如果Z