Leetcode关于数对的 题总结

大三了,要找工作了,呜呜所以来水一下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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值