You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// 计算最小合并序列长度 minRunLengthmacroComputeMinRunLength(nArg: Smi): Smi{
let n: Smi=nArg;
let r: Smi=0;// Becomes 1 if any 1 bits are shifted off.assert(n>=0);// 如果小于 64 ,则返回n(该值太小,无法打扰那些奇特的东西)// 否则不断除以2,得到结果在 32~64 之间while(n>=64){r=r|(n&1);n=n>>1;}constminRunLength: Smi=n+r;assert(nArg<64||(32<=minRunLength&&minRunLength<=64));returnminRunLength;}
// 计算第一个 run 的长度macroCountAndMakeRun(implicitcontext: Context,sortState: SortState)(lowArg: Smi,high: Smi): Smi{assert(lowArg<high);// 这里保存的才是我们传入的数组数据constworkArray=sortState.workArray;constlow: Smi=lowArg+1;if(low==high)return1;
let runLength: Smi=2;constelementLow=UnsafeCast<JSAny>(workArray.objects[low]);constelementLowPred=UnsafeCast<JSAny>(workArray.objects[low-1]);// 调用比对函数来比对数据letorder=sortState.Compare(elementLow,elementLowPred);// TODO(szuend): Replace with "order < 0" once Torque supports it.// Currently the operator<(Number, Number) has return type// 'never' and uses two labels to branch.constisDescending: bool=order<0 ? true : false;
let previousElement: JSAny=elementLow;// 遍历子数组并计算 run 的长度for(letidx: Smi=low+1;idx<high;++idx){constcurrentElement=UnsafeCast<JSAny>(workArray.objects[idx]);order=sortState.Compare(currentElement,previousElement);if(isDescending){if(order>=0)break;}else{if(order<0)break;}previousElement=currentElement;++runLength;}if(isDescending){ReverseRange(workArray,lowArg,lowArg+runLength);}returnrunLength;}
// 调整 pendingRuns ,使栈长度大于3时,所有 run 都满足 run[n]>run[n+1]+run[n+2] 且 run[n+1]>run2[n+2]transitioningmacroMergeCollapse(context: Context,sortState: SortState){constpendingRuns: FixedArray=sortState.pendingRuns;// Reload the stack size because MergeAt might change it.while(GetPendingRunsSize(sortState)>1){
let n: Smi=GetPendingRunsSize(sortState)-2;if(!RunInvariantEstablished(pendingRuns,n+1)||!RunInvariantEstablished(pendingRuns,n)){if(GetPendingRunLength(pendingRuns,n-1)<GetPendingRunLength(pendingRuns,n+1)){--n;}MergeAt(n);// 将第 n 个 run 和第 n+1 个 run 进行合并}elseif(GetPendingRunLength(pendingRuns,n)<=GetPendingRunLength(pendingRuns,n+1)){MergeAt(n);// 将第 n 个 run 和第 n+1 个 run 进行合并}else{break;}}}
The text was updated successfully, but these errors were encountered:
以上是常见的几种排序算法,首先思考一下,
Array.prototype.sort()
使用了上面的那种算法喃?Array.prototype.sort()
V8 种的 Array.prototype.sort()
关于
Array.prototype.sort()
,ES 规范并没有指定具体的算法,在 V8 引擎中, 7.0 版本之前 ,数组长度小于10时,Array.prototype.sort()
使用的是插入排序,否则用快速排序。在 V8 引擎 7.0 版本之后 就舍弃了快速排序,因为它不是稳定的排序算法,在最坏情况下,时间复杂度会降级到 O(n2)。
于是采用了一种混合排序的算法:TimSort 。
这种功能算法最初用于Python语言中,严格地说它不属于以上10种排序算法中的任何一种,属于一种混合排序算法:
在数据量小的子数组中使用插入排序,然后再使用归并排序将有序的子数组进行合并排序,时间复杂度为
O(nlogn)
。什么是 TimSort ?
在 解答 v8 sort 源码前,我们先看看 TimSort 具体是如何实现的,帮助我们阅读源码
Timsort 是 Tim Peter 在 2001 年为 Python 语言特意创造的,主要是 基于现实数据集中存在者大量的有序元素(不需要重新排序) 。 Timsort 会遍历所有数据,找出数据中所有有序的分区(run),然后按照一定的规则将这些分区(run)归并为一个。
具体过程为:
_runs_
,一个 run 可以认为是已经排序的小数组,也包括以逆向排序的,因为这些数组可以简单地翻转(reverse)就成为一个run如何避免归并长度相差很大 run 呢?在 Timsort 排序过程中,会存在一个栈用于记录每个 run 的起始索引位置与长度, 依次将 run 压入栈中,若栈顶 A 、B、C 的长度
|C| > |B| + |A|
|B| > |A|
在上图的例子中,因为
| A |> | B |
,所以B被合并到了它前后两个runs(A、C)中较小的一个| A |
,然后| A |
再与| C |
。 依据这个法则,能够尽量使得大小相同的 run 合并,以提高性能。注意Timsort是稳定排序故只有相邻的 run 才能归并。所以,对于已经排序好的数组,会以 O(n) 的时间内完成排序,因为这样的数组将只产生单个 run ,不需要合并操作。最坏的情况是 O(n log n) 。这样的算法性能参数,以及 Timsort 天生的稳定性是我们最终选择 Timsort 而非 Quicksort 的几个原因。
手写一个 Array.prototype.sort() 实现
了解的 Timsort 的基本思想与排序过程后,我们手写一个简易版的 Timsort :
简易版的,完整的实现可以查看 v8 array-sort 实现,下面我们就来看一下
v8 中的 Array.prototype.sort() 源码解读
即 TimSort 在 v8 中的实现,具体实现步骤如下:
核心源码解读
下面重点解读 3 个核心函数:
ComputeMinRunLength
:用来计算minRunLength
CountAndMakeRun
:计算第一个run
的长度MergeCollapse
:调整pendingRuns
,使栈长度大于3
时,所有run
都满足run[n]>run[n+1]+run[n+2]
且run[n+1]>run2[n+2]
The text was updated successfully, but these errors were encountered: