二分插入合并有序数组

184 阅读3分钟

二分插入合并有序数组

题目

两个从小到大有序的整型数组A和B,将其合并为C,仍保持从小到大有序

const [A, B] = [[1, 3, 5, 7],[2, 4, 6, 8]]
// 合并C = 【1, 2, 3, 4, 5, 6, 7, 8】

es6实现

const C = [...A, ...B].sort((a, b) => a - b)

二分法插入排序

要求:用二分法插入排序的方式实现

概念

二分法插入排序,简称二分排序,是在插入第i个元素时,对前面的0~i-1元素进行折半,先跟他们中间的那个元素比,如果小,则对前半再进行折半,否则对后半进行折半,直到left<right,然后再把第i个元素前1位与目标位置之间的所有元素后移,再把第i个元素放在目标位置上。

编码

1.编写函数merged(),遍历待插入数组,依次调用插入方法插入目标数组arr

function merged(a, b) {
    const arr = [...a] //避免修改原数组
    for (const num of b){
        insertBtoA(arr, num) // 合并元素到数组的方法
    }
    return arr
}

2.编写insertBtoA函数,已知arr为有序数组,利用二分插入排序法将目标元素插入到数组中

function insertBtoA(arr, target) {
    let left = 0
    let right = arr.length - 1
    let mid = Math.floor(arr.length - 1 / 2)
    while(left <= right) { // 查找结束条件
        mid = left + Math.floor((right - left) / 2)
        if (arr[mid] === target) {
            // 相等可直接插入
            arr.splice(mid, 0, target)
            return
        } else if (arr[mid] > target) {
            // 目标元素需插入到前半段
            right = mid - 1
        } else {
            // 目标元素与需插入到后半段
            left = mid + 1
        }
    }
    arr.splice(left, 0, target)
}

3.调用merged方法

const C = merged(A,B)
console.log(C)  // [1, 2, 3, 4, 5, 6, 7, 8]

优化

根据已知条件B也为有序数组,可知迭代插入的下一个目标元素都在本次目标元素的右侧,所以可以返回left当做下次迭代的初始left

编码

1.修改insertBtoA方法,返回left

function insertBtoA(arr, target, left) {
    let right = arr.length - 1
    let mid = Math.floor(arr.length - 1 / 2)
    while(left <= right) { // 查找结束条件
        mid = left + Math.floor((right - left) / 2)
        if (arr[mid] === target) {
            // 相等可直接插入
            arr.splice(mid, 0, target)
            return ++mid // 因为上一步已将target插入目标数组,且已知target所在数组为有序数组,故下一个target直接将mid+1作为初始left
        } else if (arr[mid] > target) {
            // 目标元素需插入到前半段
            right = mid - 1
        } else {
            // 目标元素与需插入到后半段
            left = mid + 1
        }
    }
    arr.splice(left, 0, target)
    return ++left //因为上一步已将target插入目标数组,且已知target所在数组为有序数组,故下一个target直接将left+1作为初始left
}

2.修改merged方法,获取返回left传入下次迭代调用insertBtoA中

function merged(a, b) {
    const arr = [...a] //避免修改原数组
    let left = 0
    for (const num of b){
        console.log('本次初始left:' + left)
        left = insertBtoA(arr, num, left) // 合并元素到数组的方法
    }
    return arr
}

3.调用merged方法

const C = merged(A,B)
console.log(C)
// 本次初始left:0
// 本次初始left:2
// 本次初始left:4
// 本次初始left:6
// [1, 2, 3, 4, 5, 6, 7, 8]

如果是多个有序数组的合并排序呢

已知二维数组如下:

const D = [[1, 5, 9, 12],[2, 4, 4, 8],[0, 3, 11, 19]]
// 求 C = [0,1,2,3,4,4,5,8,9,11,12,19]
// es6
// const C = D.flat().sort((a, b) => a - b)

思路

借助es6数组方法reduce,将子元素数组依次调用前面二分插入合并的方法

编码

1.reduce迭代

const C = D.reduce((a, b) => {
    return merged(a, b)
})

2.完整代码

const D = [[1, 5, 9, 12],[2, 4, 4, 8],[0, 3, 11, 19]]
function merged(a, b) {
    const arr = [...a] //避免修改原数组
    let left = 0
    for (const num of b){
        console.log('本次初始left:' + left)
        left = insertBtoA(arr, num, left) // 合并元素到数组的方法
    }
    return arr
}
function insertBtoA(arr, target, left) {
    let right = arr.length - 1
    let mid = Math.floor(arr.length - 1 / 2)
    while(left <= right) { // 查找结束条件
        mid = left + Math.floor((right - left) / 2)
        if (arr[mid] === target) {
            // 相等可直接插入
            arr.splice(mid, 0, target)
            return ++mid // 因为上一步已将target插入目标数组,且已知target所在数组为有序数组,故下一个target直接将mid+1作为初始left
        } else if (arr[mid] > target) {
            // 目标元素需插入到前半段
            right = mid - 1
        } else {
            // 目标元素与需插入到后半段
            left = mid + 1
        }
    }
    arr.splice(left, 0, target)
    return ++left //因为上一步已将target插入目标数组,且已知target所在数组为有序数组,故下一个target直接将left+1作为初始left
}
const C = D.reduce((a, b) => {
    return merged(a, b)
})
console.log(C) // [0, 1, 2, 3, 4, 4, 5, 8, 9, 11, 12, 19]