大三了,要找工作了,呜呜所以来水一下leetcode
1877求数对相关的题总结
难度依次增加(三个题)
数组中最大数对和的最小值
题解:需要找到小的最大数对和
我们贪心的想,一个数列有很多元素,它们应该是有次序的,我们应该让最大的和最小的结合,然后依次向中间靠拢才能使整个数列数对的和最小(证明:如果最大值结合其他选手一定使结果变大了,然后最大确定了依次就是次大它所结合的也就确定了)
这也算是双指针的方法
代码
class Solution {
public:
int minPairSum(vector<int>& nums) {
sort(nums.begin(),nums.end());
int sum=0;
for(int i=0;i<nums.size();i++)
{
sum=max(nums[i]+nums[nums.size()-i-1],sum);
}
return sum;
}
};
##1679. K 和数对的最大数目
解析:注意下后面给了范围所有元素都是>=1,对于这种我们需要找 和为k 的想法
最朴素:通俗的想法就是枚举,先枚举一个a在去找另外一个k-a,复杂度为O(n^2)
我们在想一下:我们枚举了一个a,我们只需要找到数组里面是否存在k-a就行,如果我们能快速知晓是否有k-a那么我们的算法就提升了
1.我们可以用数组vis[i]来标记 i 出现的次数,但是我们看题目的范围是1<=nums[i]<=1e9,所以空间太大了,不能够实现(如果题目改变范围就可以用这个方法类似于桶排序)
2 所以我们再继续想 nums.size的大小为 1e5 ,所以我们可以用map来记录数字nums[i]出现的次数,map可以让我们以O(1)的速度访问,所以提升了
这个题有特殊的一点,所有nums[i]都为正数 ,所以我们枚举a 的范围在 0—k中间(排序就找到哪些在这个范围了),我们先保存起来然后在去找 b=k-a,发现b的范围也在我们枚举的a内,直接计算就行 复杂度O(n log n)二分的复杂度(二分前提需要排序哦排序也是和二分一样复杂度,所以总的还是那么多)
下面看代码(可能比较丑,用了c++11)
想一想为什么要除以2
//map存一下
class Solution {
public:
int maxOperations(vector<int>& nums, int k) {
int cnt=0;
map<int ,int > map1;
for(int i=0;i<nums.size();i++)
if(nums[i]<k) map1[nums[i]]++;
for(auto it : map1){
cnt+=min(it.second,map1[k-it.first]);
}
return cnt/2;//算了俩次
}
};
3.我们想我们枚举a,b,先枚举a然后我们需要寻找k-a是否在数列里面,是不是很熟悉?
查询数当然用 二分查找了,但是我们要注意的是k-a这个数可能出现多次,所以我们找到一个就标记下,然后下次不能找不就行了
二分答案也行,找最左和最右的数来(就不展开了)
因为我没有去实现orz
面试题 16.24. 数对和
前面我们学习了和为k的数对都有多少个那这里就是多了一步把它存下来,所以直接放代码了(写了俩种,第一种双指针挺巧的第二种就常规操作,看的题解然后写的)
代码方法1:
还是先排序哈
看指针那里
1、若nums[l] + nums[r] == target,则记录答案,并将左指针l++,右指针r–
2、若nums[l] + nums[r] > target,则说明此时结果太大了,则r–可以将结果缩小(后面r都无用了,去结合只能让结果更大,要找到和为k只有在更小的范围里面)
3、若nums[l] + nums[r] < target,则说明此时结果太小了,则l++可以将结果增大(与上面解释一样)
有人说不懂,可能是没有找到所有的,但是找到了(我不知道能讲懂不- _ -)
对于所有ai + bi = k 的数对肯定有次序关系,那么我们假设找到了一个数对 然后它的位置为 l和r
它让l++ ,那么我们找的是 更大的a的数对,那么它对应的b肯定是在更小的r所以肯定是对的,那对于之前的数对,在之前的寻找中肯定都被我寻找到了(递归一样。。)
当两个指针重合了 就结束,否则我们就会寻找重复了
class Solution {
public:
vector<vector<int>> pairSums(vector<int>& nums, int target) {
vector<vector<int>> G;
sort(nums.begin(),nums.end());
int l=0,r=nums.size()-1;
//双指针O(n)
while (l < r) {
int flag=nums[l]+nums[r]-target;
if (flag==0) {
G.push_back({nums[l], nums[r]});
l++,r--;
}
else if(flag>0)
r--;
else
l++;
}
return G;
}
};
方法2:(常规就不解释了)
一点点细节,开始时wa了一发,,
class Solution {
public:
vector<vector<int>> pairSums(vector<int>& nums, int target) {
vector<vector<int>> G;
map<int ,int > map1;
int cnt=0;
sort(nums.begin(),nums.end());
//可能有负数
for(int i=0;i<nums.size();i++)
map1[nums[i]]++;
for(auto it : map1){
cnt=min(it.second,map1[target-it.first]);
//特例
if(it.first*2==target) cnt/=2;
while(cnt--) G.push_back({it.first,target-it.first});
map1[target-it.first]=0;
}
return G;
}
};