v8--sort方法源码中对于长度较短的数组使用的是插入排序法。
部分源码:
function InsertionSort(a, from, to) { for (var i = from + 1; i < to; i++) { var element = a[i]; // Pre-convert the element to a string for comparison if we know // it will happen on each compare anyway. var key = (custom_compare || %_IsSmi(element)) ? element : ToString(element); // place element in a[from..i[ // binary search var min = from; var max = i; // The search interval is a[min..max[ while (min < max) { var mid = min + ((max - min) >> 1); var order = Compare(a[mid], key); if (order == 0) { min = max = mid; break; } if (order < 0) { min = mid + 1; } else { max = mid; } } // place element at position min==max. for (var j = i; j > min; j--) { a[j] = a[j - 1]; } a[min] = element; } }
源码的理解:
参数:a--数组。form起点索引即0。to终点索引。
源码对数组长度进行了计算,如值为undefined会被移至数组末尾并且长度会减去。
通常如果是正经的数组to就是a.length。
需要注意的是,以上都仅仅是在初始数组长度小于22时的情况。
此插入排序法是将待比较的数值与外层循环当前值对比,外层循环是从第二个数值开始的。
开始循环数组,索引初值为1。创建一个哨兵,保存当前循环索引对应的值 。
即key = a[i],该值需要插入到已循环数组的某个位置。
创建max,记录已循环次数。即 max = i
创建min,记录插入位置。初值是0 即 min = 0
内层循环1,目的找出插入的位置。循环条件 min < max
该循环内,创建mid 保存min和max的中值。然后计算该中值对应的数组值与哨兵的大小。即compare(arr[mid], key)
需要注意的是,mid min max 都是数组的索引。
如果计算结果为0,即说明索引中值对应值与哨兵值相等。
那么插入位置就是这个点。把mid赋值给min,取得插入点。退出该循环
如果计算值小于0。比如已经排序好的有序数组个数是15。索引中值是7。a[7]与key(即a[16])比较小于0。即key大于a[7]
说明key应该要插入到a[7]后面。则插入点最起码应该在索引7后面。故令min = 7 + 1;然后继续循环找出最合适的位置。
如果计算值大于0,还是上面那个例子。a[7]与key(即a[16])比较大于0。即key小于a[7]
说明key应该要插入到a[7]前面。则插入点最起码应该在索引7前面。故令max = mid;然后继续循环找出最合适的位置。
该循环一直到min = max后结束。取得min值。
接着以当前已循环次数i作为初始值,另存副本j = i,以j > min作为判断条件。
依次把前一索引值赋值给当前索引值,如 a[16] = a[15],a[15] = a[14] 等等等
最后把哨兵赋值给插入点。即a[min] = key
至于排序是为升降序。则由比较函数的判断
如最简单的比较函数:(x, y) => return x - y
返回参数1 减去 参数2 的值。即索引中值对应值减去哨兵
索引中值对应值小于哨兵,则计算结果小于0。按照上述逻辑,把mid+1赋值给min。则插入点在该索引后面,故为升序
索引中值对应值大于哨兵,则计算结果大于0。按照上述逻辑,把mid赋值给max。则插入点在该索引前面,还是升序
图解
代码:
function insertSort(arr) { for(let i= 1; i < arr.length; i++) { let key = arr[i] // 哨兵,保存当前循环索引对应的值,需要插入到已循环数组中 let min = 0 // 插入位置 let max = i // 已循环次数 // 遍历已循环的数组,找寻插入位置 while(min < max) { // 中值 >>有符号右移 类似于 Math.floor(int/2) int是正整数 let mid = min + ((max - min) >>1) // 比较函数,传入索引中值对应的值和哨兵 const order = compare(a[mid], key) // 如果等于0 if (order === 0) { // 把mid赋值给min。作为插入位置,退出循环 min = max = mid break; } // 小于0。插入位置在该索引中值后面 if (order < 0) { min = mid + 1 } else { // 大于0。插入位置在该索引中值前面 max = mid } } for (let j = i; j > min; j--) { arr[j] = arr[j - 1] }
// 插入 arr[min] = key } }
调试: