📜  在两个数组中查找和最小的 k 对 | 2套(1)

📅  最后修改于: 2023-12-03 15:37:39.717000             🧑  作者: Mango

在两个数组中查找和最小的 k 对

给定两个长度为 $m$ 和 $n$ 且元素已按升序排好序的整数数组 nums1 和 nums2,以及一个整数 k。定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。找到和最小的 k 对数字 (u1,v1), (u2,v2) ... (uk,vk)。

示例

输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3

输出: [1,2],[1,4],[1,6]

解释: 返回序列中的前 3 对数: [1,2],[1,4],[1,6] 它们的和分别为 3, 5, 7,最小的和为 3。

解法

有多种解法可以解决该问题,包括暴力、优先队列和归并排序法等。其中,归并排序法是最优解,时间复杂度为 $O(nlogn)$。

归并排序法

对于两个有序数组 nums1 和 nums2,假设 nums1 中的第 i 个数和 nums2 中的第 j 个数组成了和最小的一对数。则,这个数对一定满足以下两个条件:

  1. i 和 j 的和满足 $i+j=k$ 的限制。
  2. nums1[i] 和 nums2[j] 是当前还未考虑过的数对中的最小值。

基于这两个条件,可以通过归并排序来解决问题。具体地,我们可以按以下方式进行归并:

  1. 我们需要维护一个指针数组 idx,其中 idx[i] 表示 nums1 第 i 个元素与 nums2 某个元素组成的数对中,nums1 中的元素下标为 idx[i],nums2 中的元素下标为 i。
  2. 初始化指针数组:idx[0...k-1] 分别为 0,然后将数组idx排序。同时,我们将 nums1[0]、nums2[0]、idx[0] 放到一个三元组数组中,再按照 nums1[i] + nums2[j] 的大小关系排序。
  3. 对于排好序后的三元组数组的每个元素,记录 nums1[i]+nums2[j] 的值 sum 以及 idx[i]+1,并更新指针 idx[i] 的值。将这些元素作为输出数组的元素。
  4. 重复步骤 3 直到找到 k 对数字。

例子:

输入:

nums1 = [1,7,11], nums2 = [2,4,6], k = 3

解释:

首先,将指针数组初始化为 [0, 0, 0]。然后,生成三元组数组 [(1+2, 0+1, [1, 2]), (7+2, 0+1, [1, 3]), (11+2, 0+1, [1, 4]), (1+4, 1+1, [2, 2]), (7+4, 1+1, [2, 3]), (11+4, 0+2, [2, 4]), (1+6, 2+1, [3, 2]), (7+6, 1+2, [3, 3]), (11+6, 0+3, [3, 4])] 并按照和的大小排序,得到 [(1+2, 0+1, [1, 2]), (1+4, 1+1, [2, 2]), (7+2, 0+1, [1, 3]), (7+4, 1+1, [2, 3]), (1+6, 2+1, [3, 2]), (11+2, 0+1, [1, 4]), (7+6, 1+2, [3, 3]), (11+4, 0+2, [2, 4]), (11+6, 0+3, [3, 4])]。最后,生成输出数组 [(1, 2), (1, 4), (1, 6)]。

代码如下:

from typing import List


def kSmallestPairs(nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
    if not nums1 or not nums2 or not k: 
        return []
    n1, n2 = len(nums1), len(nums2)
    if k > n1 * n2: 
        k = n1 * n2

    idx = [0] * n1
    res = []
    for _ in range(k):
        min_sum, idx_min = float('inf'), -1
        for i in range(n1):
            if idx[i] < n2 and (s := nums1[i] + nums2[idx[i]]) < min_sum:
                min_sum = s
                idx_min = i
        if idx_min == -1:
            break
        res.append([nums1[idx_min], nums2[idx[idx_min]]])
        idx[idx_min] += 1
    return res
复杂度
  • 时间复杂度:$O(klogk)$
  • 空间复杂度:$O(k)$
总结

本题中,我们介绍了如何使用归并排序来找到和最小的 k 对数。此外,我们还讨论了暴力和优先队列等其他解法。在实际工程中,我们根据具体情况选择合适的解法,以达到最优化的效果。