function mergeSort(arr) { let { length } = arr if (length < 2) { return arr } let midIndex = Math.floor(length / 2) let leftArr = mergeSort(arr.slice(0, midIndex)) let rightArr = mergeSort(arr.slice(midIndex)) return merge(leftArr, rightArr) } function merge(leftArr, rightArr) { let i = 0, j = 0, arr = [] while (true) { if (i === leftArr.length) { arr.push(...rightArr.slice(j)) break } if (j === rightArr.length) { arr.push(...leftArr.slice(i)) break } if (leftArr[i] < rightArr[j]) { arr.push(leftArr[i]) i++ } if (rightArr[j] <= leftArr[i]) { arr.push(rightArr[j]) j++ } } return arr } let arr = [1, 5, 2, 11, 7, 3, 1, 6, 17, 10] let arrInOrder = mergeSort(arr) console.log(arrInOrder) // [ 1, 1, 2, 3, 5, 6, 7, 10, 11, 17 ]
優化算法的指導思想之一,找到某些能夠簡化處理的特殊狀況算法
當 leftArr 的最後一個元素小於 rightArr 的第一個元素時,那麼順序就應該是 [leftArr, rightArr]數組
當 rightArr 的最後一個元素小於 leftArr 的第一個元素時,那麼順序就應該是 [rightArr, leftArr]函數
因此修改 mergeSort 函數以下優化
function mergeSort(arr) { let { length } = arr if (length < 2) { return arr } let midIndex = Math.floor(length / 2) let leftArr = mergeSort(arr.slice(0, midIndex)) let rightArr = mergeSort(arr.slice(midIndex)) if (leftArr[leftArr.length - 1] <= rightArr[0]) { return [...leftArr, ...rightArr] } if (rightArr[rightArr.length - 1] <= leftArr[0]) { return [...rightArr, ...leftArr] } return merge(leftArr, rightArr) } ...
當數組的長度變小到必定程序時,採用插入排序code
先修改代碼以下排序
function mergeSort(arr, fromIndex, length) { if (length < 2) { return } mergeSort(arr, fromIndex, Math.floor(length / 2)) mergeSort(arr, fromIndex + Math.floor(length / 2), length - Math.floor(length / 2)) merge(arr, fromIndex, length) } function merge(arr, fromIndex, length) { let leftArr = arr.slice(fromIndex, fromIndex + Math.floor(length / 2)) let rightArr = arr.slice(fromIndex + Math.floor(length / 2), fromIndex + length) let i = 0, j = 0, orderedArr = [] while (true) { if (i === leftArr.length) { orderedArr.push(...rightArr.slice(j)) break } if (j === rightArr.length) { orderedArr.push(...leftArr.slice(i)) break } if (leftArr[i] < rightArr[j]) { orderedArr.push(leftArr[i]) i++ } if (rightArr[j] <= leftArr[i]) { orderedArr.push(rightArr[j]) j++ } } arr.splice(fromIndex, length, ...orderedArr) } let arr = [1, 5, 2, 11, 7, 3, 1, 6, 17, 10] mergeSort(arr, 0, arr.length) arr // [ 1, 1, 2, 3, 5, 6, 7, 10, 11, 17 ]
把傳過的參數都記錄下來,存在 argsArr 中遞歸
例如在計算 fromIndex 爲 0, length 爲 10 時,分爲三步io
因爲尾遞歸調用,只能先計算 mergeSort(arr, 0, 5, argsArr)console
而把 [0, 10, 5, 5] 存起來,前兩個參數是merge的參數,後兩個是mergeSort的參數function
用過的參數就把它去掉,因此[0, 10, 5, 5] => [0, 10] =>
function mergeSort(arr, fromIndex, length, argsArr) { if (length < 2) { let args = argsArr.pop() while (args) { if (args.length === 4) { argsArr.push([args[0], args[1]]) break } if (args.length === 2) { merge(arr, args[0], args[1]) args = argsArr.pop() } } if (args) { return mergeSort(arr, args[2], args[3], argsArr) } else { return } } argsArr.push([fromIndex, length, fromIndex + Math.floor(length / 2), length - Math.floor(length / 2)]) return mergeSort(arr, fromIndex, Math.floor(length / 2), argsArr) } function merge(arr, fromIndex, length) { let leftArr = arr.slice(fromIndex, fromIndex + Math.floor(length / 2)) let rightArr = arr.slice(fromIndex + Math.floor(length / 2), fromIndex + length) let i = 0, j = 0, orderedArr = [] while (true) { if (i === leftArr.length) { orderedArr.push(...rightArr.slice(j)) break } if (j === rightArr.length) { orderedArr.push(...leftArr.slice(i)) break } if (leftArr[i] < rightArr[j]) { orderedArr.push(leftArr[i]) i++ } if (rightArr[j] <= leftArr[i]) { orderedArr.push(rightArr[j]) j++ } } arr.splice(fromIndex, length, ...orderedArr) } let arr = [1, 5, 2, 11, 7, 3, 1, 6, 17, 10, 312, 312, 1, 1, 2323, 4, 56, 3, 14, 5543] mergeSort(arr, 0, arr.length, []) console.log(arr)
其中 merge 函數不變,修改 mergeSort 函數
function mergeSort(arr) { for (let size = 1; size < arr.length; size = size * 2) { for (let i = 0; i + size < arr.length; i = i + size * 2) { merge(arr, i, size * 2) } } } function merge(arr, fromIndex, length) { ... }