题目

给定两个以 非递减顺序排列 的整数数组 nums1nums2 , 以及一个整数 k

定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2

请找到和最小的 k 个数对 (u1,v1), (u2,v2)(uk,vk)

示例 1:

1
2
3
4
输入: nums1 = [1,7,11], nums2 = [2,4,6], k = 3
输出: [1,2],[1,4],[1,6]
解释: 返回序列中的前 3 对数:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

示例 2:

1
2
3
4
输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1]
解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

提示:

  • 1 <= nums1.length, nums2.length <= 105
  • -109 <= nums1[i], nums2[i] <= 109
  • nums1nums2 均为 升序排列
  • 1 <= k <= 104
  • k <= nums1.length * nums2.length

实现代码

优先级队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Solution {
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
// 定义最小堆
PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> {
// 构建的元素结构:new int[nums1[i], nums2[j], j]
return (a[0] + a[1]) - (b[0] + b[1]);
});
// 初始化最小堆
for (int j : nums1) {
// 这里的第三个元素用于记录链表nums2节点的索引
pq.offer(new int[]{j, nums2[0], 0});
}
List<List<Integer>> a = new ArrayList<>();
while (!pq.isEmpty() && k > 0) {
int[] poll = pq.poll();
k--;
// 堆元素数组第三个值的作用
int nextIndex = poll[2] + 1;
if(nextIndex < nums2.length){
pq.offer(new int[]{poll[0], nums2[nextIndex], nextIndex});
}
List<Integer> list = new ArrayList<>();
list.add(poll[0]);
list.add(poll[1]);
a.add(list);
}
return a;
}
}

时间复杂度

时间组成:

  • 初始化堆:假设nums1的长度为n,总共O(n*logn)
    • 每次offer操作:O(logn)
    • 一共n次
  • while循环:poll+offer = O(logn),总共O(k*logn)
    • poll操作:
      • 每次O(logn)
      • 一共k
    • offer操作
      • 可能会有一次,O(logn)

总时间复杂度:O(nlogn) + O(klogn) = O((n + k)logn)

空间复杂度

空间组成:

  • 堆:O(n)
  • 返回集合:O(k)

总空间复杂度:O(n+k),一般返回空间不计入,所以最终为O(k)