🌈【LeetCode 321. 拼接最大数 】- JavaScript(分治+单调栈)

126 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情


说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)

作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金

GitHub:P-J27、 CSDN:PJ想做前端攻城狮

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


【LeetCode 321. 拼接最大数 】- JavaScript(分治+单调栈)

给定长度分别为 m 和 n 的两个数组,其元素由 0-9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k (k <= m + n) 个数字拼接成一个新的数,要求从同一个数组中取出的数字保持其在原数组中的相对顺序。

求满足该条件的最大数。结果返回一个表示该最大数的长度为 k 的数组。

说明: 请尽可能地优化你算法的时间和空间复杂度。

示例 1:

输入: nums1 = [3, 4, 6, 5] nums2 = [9, 1, 2, 5, 8, 3] k = 5 输出: [9, 8, 6, 5, 3]

思路分析

这道题目有下面是个核心问题,解了它们这题就不在话下了。

  1. 怎么求出其中一个数组的最优解
  2. 怎么进行合并已经求出的两个数组的结果
  3. 如何进行数组的比较

可以说让人崩溃,做完这道题,相当于写了三道题。如果面试遇到这种题目,你应该懂面试官什么意思了,他对你不满意。

分而治之

思路:复杂问题解不了,我就给你拆解成一个一个小问题,解完再合并,非常经典的分治思想。

具体步骤:两者同时移动,如果后续有不同的元素则从元素更大那个数组中选,如果后续一直相等直到有一个越界,则从长度更长的那个数组中选,如果同时越界显然从哪个数组中选都是可以的

var maxNumber = function (nums1, nums2, k) {
    let m = nums1.length, n = nums2.length;
    let res = new Array(k).fill(0);
    for (let i = Math.max(0, k - n); i <= k && i <= m; i++) {
        let arr = merge(maxArr(nums1, i), maxArr(nums2, k - i), k);
        if (compare(arr, 0, res, 0)) res = arr;
    }
    return res;
};
function maxArr(nums, k) {
    let n = nums.length;
    let res = new Array(k).fill(0);
    for (let i = 0, j = 0; i < n; i++) {
        while (n - i + j > k && j > 0 && nums[i] > res[j - 1]) j--;
        if (j < k) res[j++] = nums[i]
    }
    return res
}
function merge(nums1, nums2, k) {
    let res = new Array(k).fill(0);
    for (let i = 0, j = 0, r = 0; r < k; r++) {
        res[r] = compare(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
    }
    return res;
}
function compare(nums1, i, nums2, j) {
    while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) { i++; j++; }
    return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j])
}

贪心+DP

分析

动规从第一个数组取i个数,从第二个数组取k-i个数。 要取i个数,要从第一个数组取到i个最大数。取剩下K-i个数,要从第二个数组中取k-i个数。对于取到的两个vector进行组合,拿到当前组合的最大排序值。判断当前拿到的序列是否是最大序列值。

**注意:**代码虽然写起来简单,但是整体的效率没有分治来得好。可以试试,观察一下dp里面索引是怎么转化的

var maxNumber = function(nums1, nums2, k) {
    let getMaxNum = (nums, k) => {
        if (k == 0) return []
        let ans = []
        let tmp = nums.length - k
        for (let num of nums) {
            while (ans.length > 0 && num > ans[ans.length - 1] && (tmp--) > 0) {
                ans.pop()
            }
            ans.push(num)
        }
        ans = ans.slice(0, k)
        return ans
    }
    let compare = (i, j, nums1, nums2) => {
        while (i < nums1.length && j < nums2.length && nums1[i] == nums2[j]) {
            i++;
            j++;
        }
        return j == nums2.length || (i < nums1.length && nums1[i] > nums2[j]);
    }
    let formatNum = (nums1, nums2) => {
        let t1 = 0
        let t2 = 0
        let l1 = nums1.length
        let l2 = nums2.length
        let ans = []
        if (l1 == 0 || l2 == 0) return nums1.concat(nums2)
        while (t1 < l1 && t2 < l2) {
            if (compare(t1, t2, nums1, nums2)) {
                ans.push(nums1[t1])
                t1++
            } else {
                ans.push(nums2[t2])
                t2++
            }
        }

        ans = ans.concat(nums2.slice(t2)).concat(nums1.slice(t1))
        return ans
    }

    let res = []
    let n1 = nums1.length
    let n2 = nums2.length
    for (let i = 0; i <= k; i++) {
        const k1 = k - i
        if (i > n1 || k1 > n2) continue
        let v1 = res.join('')
        let v2 = formatNum(getMaxNum(nums1, i), getMaxNum(nums2, k1)).join('')
        console.log(formatNum(getMaxNum(nums1, i), getMaxNum(nums2, k1)))
        if (v1 < v2) {
            res = formatNum(getMaxNum(nums1, i), getMaxNum(nums2, k1))
        }
    }
    return res
}


感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。

写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤