You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k.

Define a pair (u,v) which consists of one element from the first array and one element from the second array.

Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums.

Example 1:

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

Return: [1,2],[1,4],[1,6]

The first 3 pairs are returned from the sequence:
[1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6]

Example 2:

Given nums1 = [1,1,2], nums2 = [1,2,3],  k = 2

Return: [1,1],[1,1]

The first 2 pairs are returned from the sequence:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]

Example 3:

Given nums1 = [1,2], nums2 = [3],  k = 3 

Return: [1,3],[2,3]

All possible pairs are returned from the sequence:
[1,3],[2,3]

Credits:
Special thanks to @elmirap and @StefanPochmann for adding this problem and creating all test cases.

这道题给了我们两个数组,让我们从每个数组中任意取出一个数字来组成不同的数字对,返回前K个和最小的数字对。那么这道题有多种解法,我们首先来看brute force的解法,这种方法我们从0循环到数组的个数和k之间的较小值,这样做的好处是如果k远小于数组个数时,我们不需要计算所有的数字对,而是最多计算k*k个数字对,然后将其都保存在res里,这时候我们给res排序,用我们自定义的比较器,就是和的比较,然后把比k多出的数字对删掉即可,参见代码如下:

解法一:

class Solution {
public:
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
vector<pair<int, int>> res;
for (int i = ; i < min((int)nums1.size(), k); ++i) {
for (int j = ; j < min((int)nums2.size(), k); ++j) {
res.push_back({nums1[i], nums2[j]});
}
}
sort(res.begin(), res.end(), [](pair<int, int> &a, pair<int, int> &b){return a.first + a.second < b.first + b.second;});
if (res.size() > k) res.erase(res.begin() + k, res.end());
return res;
}
};

我们也可以使用multimap来做,思路是我们将数组对之和作为key存入multimap中,利用其自动排序的机制,这样我们就可以省去sort的步骤,最后把前k个存入res中即可:

解法二:

class Solution {
public:
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
vector<pair<int, int>> res;
multimap<int, pair<int, int>> m;
for (int i = ; i < min((int)nums1.size(), k); ++i) {
for (int j = ; j < min((int)nums2.size(), k); ++j) {
m.insert({nums1[i] + nums2[j], {nums1[i], nums2[j]}});
}
}
for (auto it = m.begin(); it != m.end(); ++it) {
res.push_back(it->second);
if (--k <= ) return res;
}
return res;
}
};

下面这种方式用了priority_queue,也需要我们自定义比较器,整体思路和上面的没有什么区别:

解法三:

class Solution {
public:
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
vector<pair<int, int>> res;
priority_queue<pair<int, int>, vector<pair<int, int>>, cmp> q;
for (int i = ; i < min((int)nums1.size(), k); ++i) {
for (int j = ; j < min((int)nums2.size(), k); ++j) {
if (q.size() < k) {
q.push({nums1[i], nums2[j]});
} else if (nums1[i] + nums2[j] < q.top().first + q.top().second) {
q.push({nums1[i], nums2[j]}); q.pop();
}
}
}
while (!q.empty()) {
res.push_back(q.top()); q.pop();
}
return res;
}
struct cmp {
bool operator() (pair<int, int> &a, pair<int, int> &b) {
return a.first + a.second < b.first + b.second;
}
};
};

下面这种方法比较另类,我们遍历nums1数组,对于nums1数组中的每一个数字,我们并不需要遍历nums2中所有的数字,实际上,对于nums1中的数字,我们只需要记录nums2中下一个可能组成数字对的坐标,这里我们使用一个idx数组,其中idx[i]表示的数字是nums1[i]将从nums2数组上开始寻找的位置,因为 {nums1[i], nums2[i - 1]} 已经被加入到了结果res中,这种方法其实也是一种地毯式搜索,但是并不需要遍历完所有的组合,因为我们有idx数组来进行剪枝。我们suppose需要进行k次循环,但是题目中没有说我们一定能取出k对数字,而我们能取出的对儿数跟数组nums1和nums2的长度有关,最多能取出二者的长度之积的对儿数,所以我们取其跟k之间的较小值为循环次数。我们定义idx数组,长度为nums1的长度,初始化均为0。下面开始循环,在每次循环中,我们新建变量cur,记录从nums1中取数的位置,初始化为0,使用变量sum来记录一个当前最小的两数之和,初始化为正无穷。然后开始遍历数组nums1,更新sum的条件有两个,第一个是idx[i]上的数要小于nums2的长度,因为其是在nums2开始寻找的位置,当然不能越界,第二个条件的候选的两个数组 nums1[i] 和 nums2[idx[i]] 之和小于等于sum。同时满足这两个条件就可以更新sum了,同时更新cur为i,表示当前从nums1取出数字的位置。当遍历nums1的for循环结束后,此时cur的位置就是要从nums1取出的数字的位置,根据idx[cur]从nums2中取出对应的数组,形成数对儿存入结果res中,然后idx[cur]自增1,因为当前位置的数字已经用过了,下次遍历直接从后面一个数字开始吧,这是本解法的设计精髓所在,一定要弄清楚idx数组的意义,参见代码如下:

解法四:

class Solution {
public:
vector<pair<int, int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
vector<pair<int, int>> res;
int size = min(k, int(nums1.size() * nums2.size()));
vector<int> idx(nums1.size(), );
for (int t = ; t < size; ++t) {
int cur = , sum = INT_MAX;
for (int i = ; i < nums1.size(); ++i) {
if (idx[i] < nums2.size() && sum >= nums1[i] + nums2[idx[i]]) {
cur = i;
sum = nums1[i] + nums2[idx[i]];
}
}
res.push_back({nums1[cur], nums2[idx[cur]]});
++idx[cur];
}
return res;
}
};

参考资料:

https://discuss.leetcode.com/topic/50429/c-solution

https://discuss.leetcode.com/topic/50459/c-idea-of-using-multimap

https://discuss.leetcode.com/topic/50422/naive-accepted-solution-c

https://discuss.leetcode.com/topic/50421/c-priority_queue-solution

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Find K Pairs with Smallest Sums 找和最小的K对数字的更多相关文章

  1. [LeetCode] 373. Find K Pairs with Smallest Sums 找和最小的K对数字

    You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define ...

  2. 373 Find K Pairs with Smallest Sums 查找和最小的K对数字

    给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k.定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2.找到和最小的 k 对数字 (u1,v1 ...

  3. 373. Find K Pairs with Smallest Sums 找出求和和最小的k组数

    [抄题]: You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. D ...

  4. 【LeetCode】373. Find K Pairs with Smallest Sums 解题报告(Python)

    [LeetCode]373. Find K Pairs with Smallest Sums 解题报告(Python) 标签: LeetCode 题目地址:https://leetcode.com/p ...

  5. 373. Find K Pairs with Smallest Sums

    You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. 给你两个数组n ...

  6. 373. Find K Pairs with Smallest Sums (java,优先队列)

    题目: You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Def ...

  7. #Leetcode# 373. Find K Pairs with Smallest Sums

    https://leetcode.com/problems/find-k-pairs-with-smallest-sums/ You are given two integer arrays nums ...

  8. Find K Pairs with Smallest Sums -- LeetCode

    You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define ...

  9. 【leetcode】Find K Pairs with Smallest Sums

    You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. Define ...

随机推荐

  1. SpingMVC 核心技术帮助文档

    声明:本篇文档主要是用于参考帮助文档,没有实例,但几乎包含了SpringMVC 4.2版本的所有核心技术,当前最新版本是4.3,4.2的版本已经经是很新的了,所以非常值得大家一读,对于读完这篇文档感觉 ...

  2. 3.C#面向对象基础聊天机器人

    基于控制台的简单版的聊天机器人,词库可以自己添加. 聊天机器人1.0版本 源码如下: using System; using System.Collections.Generic; using Sys ...

  3. SQL Server 2012 清理日志 截断日志的方法

    MEDIA数据库名 ALTER DATABASE MEDIA SET RECOVERY SIMPLE WITH NO_WAIT ALTER DATABASE MEDIA SET RECOVERY SI ...

  4. Json map

    1. 返回数据形式 Class returnMsg{ boolean success; String   msg; String   errorMsg; } 2.问题 当msg中的数据由对象 或 集合 ...

  5. Java面试题整理一(侧重多线程并发)

    1..是否可以在static环境中访问非static变量? 答:static变量在Java中是属于类的,它在所有的实例中的值是一样的.当类被Java虚拟机载入的时候,会对static变量进行初始化.如 ...

  6. php注释规范

    注释在写代码的过程中非常重要,好的注释能让你的代码读起来更轻松,在写代码的时候一定要注意注释的规范.(李昌辉) php里面常见的几种注释方式: 1.文件头的注释,介绍文件名,功能以及作者版本号等信息 ...

  7. 理解Java对象的交互:时钟显示程序

    实现: 结构: 对象:时钟  - 对象:小时                 - 对象:分钟 小时和分钟具有相同属性(值,上限),可以用一个类Display来定义这两个对象: 但是两者之间又具有联系( ...

  8. 什么是WebPack,为什么要使用它?

    1.什么是Webpack WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等) ...

  9. 如何在SharePoint 当中使用纯JSOM上传任意二进制文件(小于2MB)

    在微软的官方网站上有关于如何在SharePoint当中使用JS创建一个简单的文本文件的例子,经过我的思考我觉得结合Html5特性的浏览器,是完全可以通过JS来读取到文件的内容的(这一部分的内容请大家自 ...

  10. InfoPath错误,此文档库已被重命名或删除

    在使用InfoPath发布表单,发布到SharePoint服务器报错,如下介绍: 环境:Windows 2012 DateCenter + Sql 2012 + SharePoint 2013 + O ...