LeetCode题练习与总结:拼接最大数--321-输入:nums1 = [3,9], nums2 = [8,9], k = 3 输出:[9,8,9] 提示:

时间:2024-10-22 18:01:36
  • m == nums1.length
  • n == nums2.length
  • 1 <= m, n <= 500
  • 0 <= nums1[i], nums2[i] <= 9
  • 1 <= k <= m + n

二、解题思路

  1. 创建一个辅助函数monoInc,该函数能够从一个数组中找出长度为k的最大单调递增子序列。这个子序列是在保持原数组元素相对顺序的情况下得到的。
  2. 创建另一个辅助函数merge,该函数能够合并两个单调递增的数组,并返回一个长度为k的最大单调递增数组。
  3. maxNumber函数中,遍历所有可能的从nums1中取i个元素,从nums2中取k-i个元素的情况(i的取值范围是0k,并且要满足i <= nums1.lengthk-i <= nums2.length),然后使用merge函数合并这些元素,并保留合并后的最大数组。

三、具体代码

class Solution {
    public int[] maxNumber(int[] nums1, int[] nums2, int k) {
        int m = nums1.length, n = nums2.length;
        int[] ans = new int[k];
        for (int i = Math.max(0, k - n); i <= k && i <= m; ++i) {
            int[] candidate = merge(monoInc(nums1, i), monoInc(nums2, k - i), k);
            if (greater(candidate, 0, ans, 0)) {
                ans = candidate;
            }
        }
        return ans;
    }

    private int[] monoInc(int[] nums, int k) {
        int n = nums.length;
        int[] res = new int[k];
        for (int i = 0, j = 0; i < n; ++i) {
            while (n - i > k - j && j > 0 && res[j - 1] < nums[i]) {
                --j;
            }
            if (j < k) {
                res[j++] = nums[i];
            }
        }
        return res;
    }

    private int[] merge(int[] nums1, int[] nums2, int k) {
        int[] res = new int[k];
        for (int i = 0, j = 0, r = 0; r < k; ++r) {
            res[r] = greater(nums1, i, nums2, j) ? nums1[i++] : nums2[j++];
        }
        return res;
    }

    private boolean greater(int[] nums1, int i, int[] nums2, int 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]);
    }
}

在这段代码中:

  • monoInc函数通过贪心算法,维护一个单调递增的序列。
  • merge函数比较两个单调递增序列的元素,并合并成一个新的单调递增序列。
  • greater函数比较两个序列的元素,如果第一个序列在某个位置上的元素更大,或者第二个序列已经结束,则返回true
  • maxNumber函数遍历所有可能的组合,并保留合并后的最大数组。

四、时间复杂度和空间复杂度

1. 时间复杂度
  • maxNumber 函数:

    • 外层循环的次数取决于nums1nums2的长度以及k的值,最坏情况下,循环次数为k
    • 在每次循环中,我们调用monoInc两次和merge一次。
    • greater函数在merge中被调用k次。
  • monoInc 函数:

    • 对于每个数组nums,我们进行一次遍历,时间复杂度为O(n),其中n是数组的长度。
    • 在每次遍历中,可能需要执行一个内部循环来移除不满足条件的元素,最坏情况下,内部循环可能执行k次。
    • 因此,monoInc函数的时间复杂度为O(nk)
  • merge 函数:

    • 这个函数对两个数组合并,每个数组长度为k,因此时间复杂度为O(k)
    • 在合并过程中,我们调用了greater函数,其时间复杂度在最坏情况下为O(k)
  • greater 函数:

    • 这个函数比较两个数组的元素,最坏情况下,它需要遍历整个数组,时间复杂度为O(k)

综合上述分析,总的时间复杂度为: O(k * (O(mk) + O(nk) + O(k) + O(k))) = O(k * (mk + nk + 2k)) = O(k^2 * (m + n + 2)) = O(k^2 * (m + n))

2. 空间复杂度
  • maxNumber 函数:

    • 需要常数空间来存储变量mnansi等。
    • 需要额外空间来存储每次merge函数的返回结果。
  • monoInc 函数:

    • 需要一个长度为k的数组res来存储结果。
  • merge 函数:

    • 需要一个长度为k的数组res来存储合并后的结果。
  • greater 函数:

    • 只需要常数空间。

综合上述分析,总的空间复杂度为:O(k),因为最大的空间占用是在monoIncmerge函数中创建的长度为k的数组。

五、总结知识点