瀏覽器排序對比-Chrome VS Firefox

做者:顏亦浠@毛豆前端javascript

Chrome瀏覽器和Firefox瀏覽器對一些算法的實現存在差別,之前遇到過一個問題,作了一個記錄,下面就和你們一塊兒探討下。前端

1、問題描述

接口返回數據相同,通過排序後,同一個頁面兩個瀏覽器展現效果不一樣。java

2、問題定位

一、前端排序算法不對git

二、其餘因素影響github

從新梳理後,發現sort用法寫的不對。算法

sort()若是不帶參數,就是按照字母順序對數組中的元素進行排序,也就是按照字母編碼的順序進行排序。chrome

若是sort()中傳入排序規則函數,則能夠自定義排序。數組

規則以下:瀏覽器

一、比較函數應該有兩個參數a,b函數

二、 若 a 小於 b,在排序後的數組中 a 應該出如今 b 以前,則返回一個小於 0 的值。若 a 等於 b,則返回 0。若 a 大於 b,則返回一個大於 0 的值。

修改以後在打斷點驗證的過程當中發現,兩個瀏覽器遍歷過程當中每一次的結果是不一樣的,雖然最終排序的結果一致。

深究發現,兩個瀏覽器內核在進行排序是所採用的算法不一樣。

火狐sort排序用的是歸併排序,chrome瀏覽器用的是插入排序、快速排序(數量小於10的數組使用 InsertionSort,比10大的數組則使用 QuickSort)

3、幾種算法的基本實現以及對比

一、歸併排序(Merge Sort)

基本思想:將兩個有序數列合併成一個有序數列,包括從上往下、從下往上兩種方式,區別在於分組大小,前者傾向於首先均分爲兩個組,後者傾向於分紅每組只有一個元素的多個組。

基本demo:

// 歸併排序的通常實現
    const merge = (left, right) => {
        const result = [];
        while (left.length && right.length) {
            if(left[0] <= right[0]) {
                result.push(left.shift()); 
            } else {
                result.push(right.shift())
            }
        }
        while(left.length) result.push(left.shift());
        while(right.length) result.push(right.shift());
        return result;
    }
    const mergeSort = arr => {
        const len = arr.length;
        if (len < 2) {
            return arr;
        }
        let middle = Math.floor(len/2);
        let left = arr.slice(0, middle);
        let right = arr.slice(middle);
        return merge(mergeSort(left), mergeSort(right))
    }
複製代碼

二、快速排序(Quick Sort)

基本思想:在數組中選擇一個基數,比基數大的放在右邊子數組,比基數小的放在左邊子數組,而後在分別對左右兩邊的數組分別執行以上操做,直到所分紅的數組都只有一個元素。

基本demo:

//快速排序的通常實現
    const quickSort1 = arr => {
    if (arr.length <= 1) {
        return arr;
    }
    const midIndex = Math.floor(arr.length / 2);
    const valArr = arr.splice(midIndex, 1);
    const midIndexVal = valArr[0];
    const left = []; 
    const right = []; 
    for (let i = 0; i < arr.length; i++) {
        if (arr[i] < midIndexVal) {
            left.push(arr[i]);
        } else {
            right.push(arr[i]);
        }
    }
    return quickSort1(left).concat(midIndexVal, quickSort1(right));
};
複製代碼

三、插入排序(Insertion Sort)

比較適用於大部分元素已經排序好的狀況。

基本思想:把整個數組拆分紅兩個子數組,一個爲按順序排好的,一個爲沒有排序的,每次從沒有排序的數組中拆除一個數,將起放入已經排序好的數組中並排好序,直到沒有排序的數組爲空爲止。

基本demo:

//插入排序的通常實現
    const insertionSort = (nums) => {
        for (let i = 1; i < nums.length; ++i) {
            let preIndex = i -1;
            let temp = nums[i];
            while (j >=0 && nums[preIndex] > temp) {
                nums[preIndex+1] = nums[preIndex];
                preIndex--;
            }
            nums[preIndex+1] = temp;
        }
        return nums;
    }
複製代碼

4、性能分析

時間複雜度:

  • 歸併排序(O(nlogn))
  • 快速排序(O(nlogn))
  • 插入排序(O(n²))

穩定性:

  • 歸併排序(穩定)
  • 快速排序(不穩定)
  • 插入排序(穩定)

優化方案:

  • 插入排序:藉助二分查找的折半插入
const binarySearch = (arr, maxIndex, value) => {
        let min = 0;
        let max = maxIndex;
        while (min <= max) {
            const mid = Math.floor((min + max) / 2);
            if (arr[mid] <= value) {
                min = mid + 1;
            } else {
                max = mid - 1;
            }
        }
        return min;
    }
const insertionSort2 = (arr) => {
        for (let i = 1, len = arr.length; i < len; i++) {
            const temp = arr[i];
            const insertIndex = binarySearch(arr, i - 1, arr[i]);
            for (let preIndex = i - 1; preIndex >= insertIndex; preIndex--) {
                arr[preIndex + 1] = arr[preIndex];
            }
            arr[insertIndex] = temp;
        }
        return arr;
    }
複製代碼
  • 歸併排序:空間優化,用array.splice 取代 array.slice,減小空間消耗;對其中規模較小的子數組,使用插入排序;
  • 快速排序:in-place
const swap = (arr, i, j) => {
        let temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    };
    const partition = (arr, left, right) => {
        let pivot = left, //設定基準值(pivot)
        index = pivot + 1;
        for (let i = index; i <= right; i++) {
            if (arr[i] < arr[pivot]) {
                swap(arr, i, index);
                index++;
            }
        }
        swap(arr, pivot, index - 1);
        return index - 1;
    }; 
    const quickSort = (arr, left, right) => {
        let len = arr.length, index;
        left = typeof left != 'number' ? 0 : left;
        right = typeof right != 'number' ? len-1 : right;
        if (left < right ) {
            index = partition(arr, left, right);
            quickSort(arr, left, index - 1);
            quickSort(arr, index + 1, right);
        }
        return arr;
    }
複製代碼
相關文章
相關標籤/搜索