array.sort源码实现

1,731 阅读5分钟

前言

之前业务需求开发需要对商品进行排序,用到了array.sort这个方法,这个方法的第二个参数是个用来设定排序方式的方法。

a, b) => {return a - b}; // 或者b-a

很多文章直接说了含义a-b表示正序,b-a表示倒序,那么这里的a和b究竟是什么东西,为什么a-b和b-a就能控制排序的方式呢?

array.sort原理解析

当数组数量小于等于10的时候,使用插入排序法进行排序。否则使用快速排序法。

对于较短的数组,插入排序法比快速排序快。

if (to - from <= 10) {
    InsertionSort(a, from, to); // 插入排序
    return;
}

插入排序

简单实现

Array.sort((a, b) => { // compareFn
    return a-b;
});

将传入的回调方法命名为compareFn。

const InsertionSort(array, from, to) {
    for (let i = from + 1; i < to; i++) {
        const current = array[i];
        for (let j = i - 1; j >= from; j--) {
            const target = j;
            const order = compareFn(target, current);
            if (order > 0) {
                array[j + 1] = target;
                array[j] = current;
            } else {
                break;
            }
        }
    }
    return array;
}

插入排序原理:

从数组的第二个元素开始向后遍历,作为插入元素。每个插入元素,遍历插入元素前面所有元素,找到适合插入元素的位子。

以数组 [3, 1, 0 , 2]正序排序为例:

首先1为插入元素,1 与3比较,3>1,1插入至3的位子。此时数组[1, 3, 0, 2]。

这回0作为插入元素,0与3比较,3>0,0插入3的位子,此时数组[1, 0, 3, 2]。

然后0与1比较,1>0,0插入1的位子,[0, 1, 3, 2];

最后2作为插入元素,2与3比较,3>2,2插入3位子,[0, 1, 2, 3]。

2与1比较, 1 < 2,停止插入。最后获得[0 , 1, 2, 3]。

javascript中array.sort,在数组长度小于等于10的时候用的是插入排序法,此时传入的函数(a, b) => {...},a是当前跟插入元素比较的元素,b是当前的插入元素,于是return a-b;在做比较的时候若插入元素小于目标元素才会进行插入,从而达成正序排序的目的。

快速排序

v8引擎中,array.sort在数组长度小于等于10的时候采用插入排序,大于10的时候采用快速排序和插入排序组合的方式。

假设有一个长度12的数组[9, 1, 0, 3, 100, 77, 4, 8, 15, 20, 23, 13];

首先取三个数,第一个,最后一个,和中间那个(第六)。分别是 9,77, 13。将这三个数先排序,结果为9、13、77,然后分别插入第一,中间和最后。结果为[9, 1, 0, 3, 100, 13, 4, 8, 15, 20, 23, 77]。

然后把之前的中间值(13)移动到第二位: [9, 13, 0, 3, 100, 1, 4, 8, 15, 20, 23, 77],并且设置一个比较值pivot(13),值为中间值。

遍历第三个到倒数第二个的元素。逻辑如下:

let low_end = from + 1;
let high_start = to - 1;
array[thirdIndex] = array[low_end]; // thirdIndex是取到的中间值的位子,例子中是6
array[low_end] = pivot; // low_end存储中间值的key,例子中是13
partition: for (let i = low_end; i < high_start; i++) {
    const elemet = array[i];
    let order = compareFn(element, pivot);
    if (order < 0) { //遍历到的元素小于中间元素,交换目标元素于中间值元素位子
        array[low_end] = array[i];
        array[i] = pivot;
        low_end = i;
    } else if (order > 0) {
        let topEle = 0;
        do { // 从后往前找到比中间值小的元素
            high_start--;
            if (high_start === i) {
                break partition;
            }
            let topEle = array[high_start];
            order = compareFn(topEle, pivot);
        } while (order > 0)
        // 将当前遍历元素和从后往前遍历到的较小(或较大)元素交换位子
        array[i] = array[high_start];
        array[high_start] = element;
        // 此时当前遍历元素变成了较小(较大)元素
        // 交换当前遍历元素和中间值位子
        array[low_end] = array[i];
        array[i] = pivot;
        low_end = i;
    }
}

v8引擎中的快速排序实现思路:先从前往后遍历找到所有比中间值小(或者大)的元素放到中间值前面,如果遇到比中间值大(或者小)的元素,此时从后往前遍历若找到比中间值小的元素将其与前序遍历的元素换位子,然后再与中间元素换位子,来达到将所有比中间值元素小的放到一边,大的放到另一边的目的。

经过一轮快速排序后,我们的数组变成了[9, 0, 3, 8,1, 4, 13, 100, 15, 20, 23, 77]。

然后在中间值位子将数组分成了两部分,[9, 0, 3, 8,1, 4]和[100, 15, 20, 23, 77]。两边进行分别排序。两部分长度都小于10,于是分别进行插入排序。若分开的几个部分长度大于10,则再次进行快速排序将数组拆成更小的部分。

最后经过三轮排序后可以得到[0, 1, 3, 4, 8, 9, 13, 15, 20, 23, 77, 100]。

招聘!!!

字节跳动互娱基础架构团队招人啦!北京、深圳、杭州都有岗位!

我们是谁

字节成立最早的前端架构团队,目前规模最大,做的最专业,手里直接有大几百人的前端业务团队,产品 DAU 上亿级别,每天不用和 PM、UI 撕逼,有良好的技术氛围,业界大牛云集,团队成员都能获得相对好的技术成长。

平时工作

负责抖音、抖音火山版、直播等业务大规模复杂业务场景的前端架构设计、实现和优化

  1. 负责PC、H5、Hybrid、App Native、BFF、RPC等一种或几种技术场景的架构;
  2. 制定开发规范,工程化体系搭建及优化,提升开发效率、质量和性能,保障业务稳定运行;
  3. 发现现有流程及架构的问题,并持续进行优化;
  4. 解决业务遇到的技术痛点和难点;
  5. 跟进业内前沿技术,保证团队技术的先进性。

职位要求

  1. 本科及以上学历,计算机及相关专业;计算机基础扎实,熟悉数据结构、网络等;
  2. 有一定的架构和方案设计能力及经验,具备一定的方案沟通和推动能力;
  3. 对后端技术有一定了解,熟悉一门后端语言(java/go等);
  4. 对前端工程化(例如构建方面:webpack、rollup等)、Nodejs、渲染框架(例如react或vue等)、中后台搭建系统等至少其一有一定深度的实践经验者优先;
  5. 有大型网站架构经验者优先;有较高的技术热情和积极性者优先。

加分项

  1. 参与或主导过优秀的开源项目;
  2. 有优秀的技术博文、博客。

有意者可以添加我微信说明来意:

image.png