算法学习记录(三十二)

92 阅读2分钟

问:

  1. 给一个包含N个整数元素的集合A,一个包含M个整数元素的集合B。定义一个操作F,从一个集合取出一个元素放到另一个集合,且操作过后两个集合的平均值都上升。问最多可以进行多少次操作F
  2. 给一个长度N的二维数组,数组中每一项有两个元素。表示有N个任务。两个元素分别代表任务开始时间和任务结束时间。假设每个任务只需要一天就可以完成,请问是否可以每个任务都按时完成。
  3. 给一个无序数组,可能有正、负、0。任选两个数字相加,请返回相加后绝对值最小的值

解: 1.

function getMaxFrequency(setA,setB) {
    let res = 0
    // 平均值
    const meanA = getMean(setA)
    const meanB = getMean(setB)
    // 当平均值一样时,无法满足条件
    if (meanA === meanB) return 0
    let bigSet = meanA > meanB ? setA : setB
    let bigMean = meanA > meanB ? meanA : meanB
    let smallSet = bigSet === setA ? setB : setA
    let smallMean = bigMean === meanA ? meanB : meanA
    // 假设是A平均值更大
    while (getMean(bigSet) > getMean(smallSet)) {
        // 找到 A 集合中的某个值 X ,这个值要满足  meanB < X < meanA。很明显,只有这样才能使得两个集合的平均值都上升。
        // 那么可以看出X的范围是两个平均值的上下限。那么A上升的越快并且B上升的越慢则X的范围越广。
        // 所以结论是取集合A中距离meanB最近 并且大于 meanB 并且不能在集合B中已存在的数,
        // 给A排序取X
        let minX = null
        bigSet = new Set(Array.from(bigSet).sort((a,b) => a-b))
        for (let i of bigSet) {
            if (i > smallMean && i < bigMean && !smallSet.has(i)) {
                minX = i
                break
            }
        }
        if (minX) {
            res++
            bigSet.delete(minX)
            smallSet.add(minX)
            continue
        }
        break
    }
    return res
    function getMean(set) {
        let mean = 0
        set.forEach((item) => {
            mean += item
        })
        mean = mean / set.size
        return mean
    }
}
  1. 遍历数组,遍历时创建两个节点,分别为开始节点和结束节点。每个节点都有任务的相关信息。把节点放入一个辅助数组,按照time排序。遍历辅助数组,当碰到开始节点时,就收集任务,把节点放入一个任务数组,并且按照任务结束时间排序。当碰到结束节点时,就按照时间差从任务数组中弹出对应数量的任务(即这么多天内做了多少任务)。如果此时任务数组中结束时间最小的任务还是小于等于当前时间,那么说明无法按时完成了,返回false。
function isCompleteTask(arr) {
    const helpArr = []
    const taskStack = []
    for (let i of arr) {
        // 开始时间节点
        const startNode = {
            time: i[0],
            end: i[1],
            type: 1 // 收集任务
        }
        // 结束时间节点
        const endNode = {
            time: i[1],
            start: i[0],
            type: 2 // 触发检查
        }
        helpArr.push(startNode,endNode)
    }
    helpArr.sort((a, b) => a.time - b.time)
    let preDay = helpArr[0].time
    for (let i of helpArr) {
        // 收集任务
        if (i.type === 1) {
            taskStack.push(i)
        }
        // 触发检查
        if (i.type === 2) {
            // 可以做几个任务
            let canDoCount = i.time - preDay
            taskStack.sort((a, b) => a.end - b.end)
            while (canDoCount) {
                taskStack.shift()
                canDoCount--
            }
            if (taskStack.length && taskStack[0].end <= preDay) {
                return false
            }
            preDay = i.time
        }
    }
    return true
}
function getMinABS(arr) {
    arr.sort((a,b) => a-b)
    let res = Infinity
    // 首先,如果全是非负数,那么数组前两个数的和就是最小的
    if (arr[0] >= 0) {
        return arr[0] + arr[1]
    }
    // 如果全是负数,那么数组最后两个数的和就是最小的

    if (arr[arr.length -1] < 0) {
        return Math.abs(arr[arr.length - 1] + arr[arr.length - 2])
    }
    // 如果有正有非负,那么其结果就是(正数+正数),(非负+非负),(非负+负数) 三种情况中的一个
    // 遍历数组,找距离每一项对应相反数最近的数。
    for (let i = 0; i <arr.length;i++) {
        const tempNear= getMostNear(arr, -arr[i], i)
        res = Math.min(Math.abs(tempNear+arr[i]), res)
    }
    return res
    function getMostNear(arr, target, ignoreIdx) {
        arr = [...arr]
        arr.splice(ignoreIdx, 1)
        let tempNear = Infinity
        let nearIdx = -1
        let left = 0
        let right = arr.length - 1
        let midIdx = Math.ceil((right + left) / 2)
        while (left <= right) {
            if (arr[midIdx] === target) {
                tempNear = arr[midIdx]
                nearIdx = midIdx
                break
            }
            if (arr[midIdx] < target) {
                left = midIdx + 1
            }
            if (arr[midIdx] > target) {
                right = midIdx - 1
            }
            if (Math.abs(target - arr[midIdx]) < Math.abs(target - tempNear)) {
                tempNear = arr[midIdx]
                nearIdx = midIdx
            }
            midIdx = Math.floor((right + left) / 2)
        }
        return tempNear
    }
}