题目描述
知识点
分类思想+堆
结果
实现
码前思考
- 碰见这个问题,我的第一感受是暴力,即
nums1
和nums2
两个数组的数匹配,然后排序。但是这实在是太暴力了,肯定会超时的,所以要想其他的巧解的方法!; - 首先我们得想我们每次都要找所有
pair(u,v)
里面最小的值min
,直到找到第k
个,那么怎么才能快速找到呢? - 其实,由于
nums1
和nums2
的升序的特性,我们将求所有pair(u,v)
里面最小的值min
转换成当前nums1
中的每个数能得到的与nums2
中的最小和的里面最小的那个。比如下面的例子:
代码实现
//对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是分类
//分类就是对结果的所有可能的结果进行分类
//所谓最小的数,就是u里面每个数的对应v里面最小的数的和的最小的数
//其实可以用优先队列的!!!
struct node{
int u; //代表下标
int v; //代表下标
int sum; //代表值
//注意要自己添上默认的构造函数
node(){}
//自己定义的构造函数
node(int _u,int _v,int _sum):u(_u),v(_v),sum(_sum){}
};
class Solution {
public:
vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
//堆的大小恒定为nums1.size
int sizeu = nums1.size();
int sizev = nums2.size();
vector<node> heap;
//结果
int sizer = min(sizeu*sizev,k);
vector<vector<int>> res(sizer);
if(sizer == 0){
return vector<vector<int>>();
}
//初始化堆,即建堆
for(int i=0;i<sizeu;i++){
heap.push_back(node(i,0,nums1[i]+nums2[0]));
}
int sizeh = sizeu;
int idx = 0;
while(idx < sizer){
//将堆顶拿出来,并且放入下一个,再向下调整
res[idx].push_back(nums1[heap[0].u]);
res[idx].push_back(nums2[heap[0].v]);
//如果v到头了
if(heap[0].v == sizev-1){
heap[0] = heap[sizeh-1];
sizeh--;
}else{
heap[0].v = heap[0].v+1;
heap[0].sum = nums1[heap[0].u] + nums2[heap[0].v];
}
//自上而下进行调整
downAdjust(heap,0,sizeh-1);
idx++;
}
return res;
}
void downAdjust(vector<node> &heap,int low,int high){
int i=low;
int j = (2*i)+1;
while(j<=high){
if(j+1 <= high && heap[j+1].sum < heap[j].sum){
j = j+1;
}
if(heap[i].sum > heap[j].sum){
//不知道swap能不能这么用
swap(heap[i],heap[j]);
i = j;
j = (2*i)+1;
}else{
break;
}
}
}
};
码后反思
-
最重要的一点就是求解本题的思想——对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是 分类 。分类就是对结果的所有可能的结果进行分类,巧妙地利用每个类别的 特殊性质 ,快速找到该类的答案,然后汇总所有的答案得到最终结果。
通过这些类别里面的值,这种思想在我们的爬楼梯有多少种方法,分糖果有多少种方法里面都有!动态规划的最优子结构也是这种思想的一种体现!
-
⭐⭐⭐⭐⭐我看网友的题解说 只要涉及到了前K个最优值的问题,一般都要想到堆 。我觉得这句话说的有道理,因为堆恰好能每次求一个最值,而不像其他排序算法要全部排完才行!
-
巩固了结构体的使用:
-
原来
swap()
不仅可以作用域基本数据类型,自己定义的结构体也可以这样使用! -
可以使用STL自带的
priority_queue
来实现堆的,使用priority_queue
修改后的代码:
//对于暴力的问题要使用自己的思考将其转换成另一个问题,最重要的思想就是分类 //分类就是对结果的所有可能的结果进行分类 //所谓最小的数,就是u里面每个数的对应v里面最小的数的和的最小的数 //其实可以用优先队列的!!! struct node{ int u; //代表下标 int v; //代表下标 int sum; //代表值 //注意要自己添上默认的构造函数 node(){} //自己定义的构造函数 node(int _u,int _v,int _sum):u(_u),v(_v),sum(_sum){} friend bool operator <(node a,node b){ return a.sum > b.sum; } }; class Solution { public: vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) { //堆的大小恒定为nums1.size int sizeu = nums1.size(); int sizev = nums2.size(); priority_queue<node> q; //结果 int sizer = min(sizeu*sizev,k); vector<vector<int>> res(sizer); if(sizer == 0){ return vector<vector<int>>(); } //初始化堆,即建堆 for(int i=0;i<sizeu;i++){ q.push(node(i,0,nums1[i]+nums2[0])); } int sizeh = sizeu; int idx = 0; while(idx < sizer){ int u = q.top().u; int v = q.top().v; res[idx].push_back(nums1[u]); res[idx].push_back(nums2[v]); //如果v到头了 if(v == sizev-1){ q.pop(); }else{ q.pop(); q.push(node(u,v+1,nums1[u]+nums2[v+1])); } idx++; } return res; } };
-
注意要考虑
nums1
和nums2
其中有一个为空的情况,这真的是LeetCode的传统艺能了,每次都栽在这上面。。。