在看数组乱序时,看到下面的实现方法不是很理解
场景
场景是用 Math.random 对数组乱序
var values = [1,2,3,4,5,6,7,8]
values.sort(function(){
return Math.random() - .5
})
console.log(values)
刚看到时,能看懂这样的做法,但并不理解这样做的原理
于是由此场景展开,分析 sort 内部排序的机制
实践
改动内部代码,打印内部参数,修改如下
values.sort(function(first, second){
var calc = Math.random() - .5
console.log(calc > 0 ? "+" : calc < 0 ? "-" : "=", first, second)
return calc
})
console.log(values)
测试环境是浏览器 chrome
打印结果为
- 2 1
+ 3 2
- 3 1
- 3 2
- 4 2
- 4 3
+ 5 2
- 5 1
+ 6 2
+ 6 1
- 7 5
- 7 3
- 7 4
- 8 2
+ 8 4
+ 8 3
[7, 4, 3, 8, 2, 5, 1, 6]
打印结果看的也是一头雾水,不知从哪分析,究其原因,是对数组 sort 方法不够了解。在看过相关文章后,理解了一些。于是做出下面的分析,属于我自己的猜想
数组 sort 方法
Array.prototype.sort()
方法可以接收一个函数
arr.sort(function(first, second){
return first - second
})
做出猜想
每次传入两个值做比较, first - second 如果大于 0 的话,函数返回一个正值,代表 first 大于 second,将 first 排在后面;小于 0 则返回一个负值,first 排在前面;等于 0 则不改变顺序
最终,数组呈现成升序 。如果内部是 second - first,则整体为倒序
规律:不管内部返回的是 first - second 还是 second - first,只要返回的值是正,在该轮比较中,first 就排在后面, 返回的值是负,first 就排在前面
当然,如何传两个值和怎么比较是 js 引擎的内部机制,我们根据猜想简要分析这一过程
分析
开始分析,原数组序列为 1 2 3 4 5 6 7 8
第一次排序,比较 2 和 1 ,返回值为负,说明是倒序,即 2 在 1 前面,此时 values 暂时排序为 2 1 3 4 5 6 7 8 ,已经确定的排序为 2 1
第二次排序,比较 3 和 2 ,返回值为正,说明是正序,即 3 在 2 后面,此时暂时排序为 2 1 3 ,问题是 1 也在 2 后面,3 也在 2 后面,不能确定 3 和 1 的位置顺序,所以,下一步继续确定 1 和 3 的顺序
第三次排序,比较 3 和 1 ,返回值为负,说明是倒序,即 3 在 1 前面,而 2 也在 1 的前面,三者的顺序并不能确定,所以,下一步继续比较 3 和 2 的位置顺序
第四次排序,比较 3 和 2 ,返回值为负,倒序,3 在 2 前面,之前确定了 2 在 1 的前面,所以三者的顺序确定,即 3 2 1,此时已确定的排序为 3 2 1
第五次排序,确定 4 的位置,即确定 4 插入序列 3 2 1 的哪一位置,在比较之前得先确定 4 和谁比较 。这里选取序列的中间位置值和待比较值比较(中间位置值的选取是 v8 中插入排序的实现,没有进一步研究,这里直接给出),比较 4 和 2 ,负 ,4 在 2 前面,此时也不知道 4 和 3 的位置顺序,所以下一步比较 4 和 3
第六次排序,比较 4 和 3 ,负,4 在 3 前面,4 3 2 1
第七次排序,确定 5 的位置,确定 5 在 4 3 2 1 的插入位置,选取中间位置值 2 和 5 比较,正,5 在 2 后面。暂时排序为 4 3 2 5 1 。此时 1 和 5 都在 2 的后面,所以需要继续确定 5 和 1 的顺序
第八次排序,比较 5 和 1,负 ,确定 5 在 1 的前面,确定排序为 4 3 2 5 1
第九次排序,确定 6 的位置,确定 6 在序列 4 3 2 5 1 插入的位置,选取中间位置值 2 和 6 比较,正,6 在 2 后面。暂时排序为 4 3 2 5 1 6。下一步继续确定 6 的位置
第十次排序,确定 6 的位置,选取 1 和 6 比较,如果比较结果是 6 在 1 的后面,则顺序确定,6 就无需跟 5 进行比较。 比较结果为正,6 在 1 的后面。已确定排序为 4 3 2 5 1 6
第十一次排序,确定 7 的位置,选取中间位置值 5 和 7 比较,负,7 在 5 的前面,所以得继续确定 7 在 4 3 2 中的位置
第十二次排序,继续确定 7 的位置,7 和 中间位置值 3 比较结果为负,7 在 3 的前面,还得比较 7 和 4 的顺序
第十三次排序,继续确定 7 的位置,7 和 4 比较 ,负,7 在 4 的前面。目前排序已确定为 7 4 3 2 5 1 6
第十四次排序,确定 8 的位置,比较 8 和 2 ,负,8 在 2 前面
第十五次排序,继续确定 8 的位置,比较 8 和 4,正,8 在 4 的后面。在 4 的 后面又在 2 的前面,所以剩一个 3 继续比较
第十六次排序,继续确定 8 的位置,比较 8 和 3,正,8 在 3 的后面。排序已确定为 7 4 3 8 2 5 1 6
所以,最终排序为 7 4 3 8 2 5 1 6
最后
到了这里,算是有些理解 sort 内部排序的机制和理解开头场景乱序做法的原理
以上分析,是结合打印的值做出的猜想。
这篇文章并没有深入研究 v8 在 sort 方法中使用的插入排序算法实现
相关文章链接
最后放上权威的 MDN Array.prototype.sort()