321. 拼接最大数

155 阅读1分钟

给定长度分别为 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]

示例 2:

输入:
nums1 = [6, 7]
nums2 = [6, 0, 4]
k = 5
输出:
[6, 7, 6, 0, 4]

示例 3:

输入:
nums1 = [3, 9]
nums2 = [8, 9]
k = 3
输出:
[9, 8, 9]

题解:

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number} k
 * @return {number[]}
 */
var maxNumber = function (nums1, nums2, k) {
    // 求出每个数组中的最大序列
    var pickmax = (nums, k) => {
        let stack = [];
        let drop = nums.length - k;
        for (const num of nums) {
            while (stack && stack[stack.length - 1] < num && drop) {
                stack.pop();
                drop--;
            }
            stack.push(num);
        }
        return stack.slice(0, k);
    }
    // 合并成一个最大序列
    var merge = (n1, n2) => {
        let res = []
        // 永远比第一个
        while (n1.length > 0 || n2.length > 0) {
            bigger = n1 > n2 ? n1 : n2;
            res.push(bigger[0]);
            bigger.shift();
        }
        return res;
    }

    let res = [];
    for (let i = 0; i <= k; i++) {
        if (i <= nums1.length && k - i <= nums2.length) {
            res.push(merge(pickmax(nums1, i), pickmax(nums2, k - i)));
        }
    }
    // 把所有的最大序列存入数组进行比较,最大值即为答案
    return res.sort((a, b) => {
        if (a > b)
            return -1;
        else if (a < b)
            return 1;
        else
            return 0;
    })[0];
};
/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @param {number} k
 * @return {number[]}
 */

// 贪心算法
// 解题思路
// 枚举:从2个序列抽出k个数,[0, k]间取i
// 序列1抽i个,还需i <= 序列1长度
// 序列2抽k - i个,还需k - i <= 序列2长度
// 与[0, k]合并:i >= 0 && i >= 序列2长度 - k 且 i <= k && i <= 序列1长度
// >= 取最大,<= 取最小。分别作为循环i的初始位置和结束位置
// 合并序列1抽i个的子序列 和 序列2抽k - i个的子序列。结果集记录合并序列
// 循环i,如果后面合并序列大于结果集记录序列,更新结果集。返回结果集即可

var maxNumber = function (nums1, nums2, k) {
    var r = Array(k).fill(0), end = Math.min(k, nums1.length)
    for (var i = Math.max(0, k - nums2.length); i <= end; i++) {
        r = max(r, merge(maxSubsequence(nums1, i), maxSubsequence(nums2, k - i)))
    }
    return r
};
// 3问:指定位数,求序列表示数最大的子序列
// 解题思路
// 表示数最大,即位数最高的数最大,即子序列的最左最大
// 序列中的数字本身没有排序,希望一次遍历时,把最大数,放到最左
// 递减单调栈的特点:遇数大,删栈中比它小的数。举例:[1, 2, 3, 0]排队进递减单调栈
// 1先进。2来了,删1。3来了,删2。0来了,不删。最终剩下[3, 0]
// 指定位数k,就是限定单调栈的深度 = k。还是上面的例子,指定位数3。余 4 - 3 = 1
// 1先进。2来了,余 1,删1,余 - 1。3来了,余 0,删2,凑不够3位,故不能删2
// 最终剩下[2, 3, 0]

var maxSubsequence = (nums, k) => {
    var mStack = [], len = nums.length, top = -1, last = len - k
    for (var i = 0; i < len; i++) {
        while (top > -1 && nums[i] > mStack[top] && last > 0) {
            //删1,预定1填空
            top--, last--
        }
        // 栈满 且 数小,剩余可用位-1
        top < k - 1 ? mStack[++top] = nums[i] : last--
    }
    return mStack
}
// 2问:合并2个序列,保持元素相对顺序,返回表示数最大的序列
// 解题思路
// 假设2个序列已经降序排列,与归并排序合并序列相同
// 只需新建结果集数组。双指针分别指两个序列开头,谁大放谁,指针右移
var merge = (nums1, nums2) => {
    var p1 = p2 = 0, r = [], len1 = nums1.length, len2 = nums2.length
    while (p1 < len1 || p2 < len2) {
        if (p2 >= len2 || nums1[p1] > nums2[p2]) {
            r.push(nums1[p1++])
            // 指针指向相同,向后找不同位
        } else if (nums1[p1] === nums2[p2]) {
            var _p1 = p1, _p2 = p2
            // 不越界,避免 undefined 循环
            while (_p1 < len1 && nums1[++_p1] === nums2[++_p2]) { }
            // p2越界放p1
            r.push(_p2 >= len2 || nums1[_p1] > nums2[_p2] ? nums1[p1++] : nums2[p2++])
        } else {
            r.push(nums2[p2++])
        }
    }
    return r
}

// 1问:2个序列长度相同,返回表示数更大的序列
// 解题思路
// 序列从左到右,表示从最高位到个位。举例:[1, 2, 3]表示123
// 序列表示的数大,即:最高位大。如果最高位相等,再比较下一位
// 下一位还相等,继续下一位,直到不相等,谁大返回谁。若都相等,返回谁都行
var max = (nums1, nums2, p1 = p2 = -1, len1 = nums1.length) => {
    // 不越界,避免 undefined === undefined 循环
    while (p1 < len1 && nums1[++p1] === nums2[++p2]) { } 
    // 找到不相等的位,该位谁大返回谁。相等返回谁都行
    return nums2[p2] > nums1[p1] ? nums2 : nums1 

}

来源:力扣(LeetCode)

链接:leetcode.cn/problems/cr…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。