给定两个以升序排列的整数数组 nums1
和 ****nums2
****, 以及一个整数 k
****。
定义一对值 (u,v)
,其中第一个元素来自 nums1
,第二个元素来自 nums2
****。
请找到和最小的 k
个数对 (u1,v1)
, (u2,v2)
... (uk,vk)
。
示例 1:
输入: 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:
输入: 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]
复制代码
示例 3:
输入: nums1 = [1,2], nums2 = [3], k = 3
输出: [1,3],[2,3]
解释: 也可能序列中所有的数对都被返回:[1,3],[2,3]
复制代码
提示:
1 <= nums1.length, nums2.length <= 104
-109 <= nums1[i], nums2[i] <= 109
nums1
,nums2
均为升序排列1 <= k <= 1000
涉及到求 k
个最值得问题,都可以利用堆这种数据结构来解题。
这里因为是要维护前 k
个最小数对,所以需要一个大顶堆。
解题思路如下:
- 实例化一个大顶堆
- 两层循环找出所有可能的数对
- 如果堆中数量不够
k
个或者当前数对之和小于堆顶数对之和,则将当前数对入堆 - 如果当前数对之和大于堆顶数对之和,则退出本次循环。因为两个数组都是升序排列的,如果当前数对之和大于堆顶数对之和,则后续数对之和肯定也大于堆顶数对之和,所以无需继续遍历。
- 最后堆中保存的就是前
k
个最小的数对
代码如下:
// 实现大顶堆
class BigHeap {
constructor(k){
this.arr = [];
this.size = 0;
this.max = k;
}
// 插入操作
push(item){
this.arr.push(item);
this.size++;
if(this.size>1){
let cur = this.size-1,
parent = (cur-1)>>1;
while(cur>0&&compare(this.arr[cur],this.arr[parent])){
[this.arr[parent],this.arr[cur]] =
[this.arr[cur],this.arr[parent]];
cur = parent;
parent = (cur-1)>>1;
}
}
if(this.size>this.max) this.pop();
}
// 弹出操作
pop(){
this.arr[0] = this.arr.pop();
this.size--;
let cur = 0,
childl = cur*2+1,
childr = cur*2+2;
while(
(childl<this.size&&compare(this.arr[childl],this.arr[cur])) ||
(childr<this.size&&compare(this.arr[childr],this.arr[cur]))
){
if(childr<this.size&&compare(this.arr[childr],this.arr[childl])){
[this.arr[cur],this.arr[childr]] =
[this.arr[childr],this.arr[cur]];
cur = childr;
}else{
[this.arr[cur],this.arr[childl]] =
[this.arr[childl],this.arr[cur]];
cur = childl;
}
childl = cur*2+1,
childr = cur*2+2;
}
}
// 获取堆顶元素
top(){
return this.arr[0]
}
}
// 比较两数组之和大小
function compare(arr1,arr2){
return (arr1[0]+arr1[1])>(arr2[0]+arr2[1])
}
var kSmallestPairs = function(nums1, nums2, k) {
// 实例化大顶堆
const heap = new BigHeap(k);
// 遍历每一种组合
for(let i = 0;i<nums1.length;i++){
for(let j = 0;j<nums2.length;j++){
const item = [nums1[i],nums2[j]];
// 只有当堆中元素数量小于k个或者当前数对之和小于堆顶元素时才插入堆中
if(heap.size<k || compare(heap.top(),item)){
heap.push(item);
}else{
// 如果当前数对大于堆顶元素,那么本次循环后续数对也一定大于堆顶元素,退出当前循环
break;
}
}
}
// 返回堆中保存数对
return heap.arr;
};
复制代码
至此我们就完成了 leetcode-373-查找和最小的K对数字
如有任何问题或建议,欢迎留言讨论!