第7天|哈希表|C++|Leetcode|454.四数相加II|383.赎金信|15.三数之和|18.四数之和

今日用时3小时😊
加油,三数之和的去重操作值得细细品味,懂得了用双指针三数之和的核心,之后n数之和的操作只不过是for的叠加

LeetCode 454.四数相加II

链接454. 四数相加 II

思路

直观思路:把逻辑上四个数组的问题转换为逻辑上两个数组的问题,问题转换为两数之和为0,较易

完整C++代码如下

//时间复杂度: O(n^2)
//空间复杂度: O(n^2)
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> sumCountAB; // 用于存储nums1和nums2中元素和的出现次数
        // 统计nums1和nums2中元素的和的出现次数
        for (int a : nums1) {
            for (int b : nums2) {
                sumCountAB[a + b]++;
            }
        }
        int count = 0; // 记录满足条件的元组个数
        // 遍历nums3和nums4中元素的和,查找其相反数出现的次数,如果存在则累加到count中
        for (int c : nums3) {
            for (int d : nums4) {
                int target = -(c + d);
                if (sumCountAB.find(target) != sumCountAB.end()) {
                    count += sumCountAB[target];
                }
            }
        }
        return count;
    }
};

LeetCode 383.赎金信

链接383. 赎金信

思路

利用数组当哈希表,存储所要求的每个字母(所需要的)的个数,再用总的magazine(我们有的)去减,被用magazine减去后char_Num中只要有字母数量大于0,说明没满足所要求的数量,返回false,如果全小于等于0,返回true

完整C++代码如下

//时间复杂度: O(n)
//空间复杂度: O(1)
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int char_Num[26]={};
		for(auto c:ransomNote){
            char_Num[c-'a']++;
        }
        for(auto c:magazine){
            char_Num[c-'a']--;
        }
        for(int n:char_Num){
			if(n>0)return false;
        }
        return true;
    }
};

LeetCode 15.三数之和

链接15. 三数之和

思路

双指针

  • 思路很简单,先对数组进行非递减排序

  • 接着设置三个指针i,l,r进行移动,每轮固定i,将l,r定在i右边的两端,向中间移动,寻求三指针指向值的和为0(如下图,图片引用代码随想录),本题的难点在于如何去重和剪枝(优化算法)。

在这里插入图片描述

完整C++代码如下

//时间复杂度: O(n^2)
//空间复杂度: O(1)
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>>res;
        sort(nums.begin(), nums.end());
        
        for(int i=0;i< nums.size();i++){
            
            if (nums[i] > 0) {
                //剪枝,i大于0,则l,r也一定大于0,直接返回
                return res;
            }
            // 去重,新一轮的nums[i]如果和前一个相等,没必要再进行此轮循环
            //因为以该大小的数作为i时,l,r所有情况在前一轮已经进行过了,直接跳过即可
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }           
            int l=i+1;
            int r = nums.size() - 1;
              while (l < r) {
				
                if (nums[i] + nums[l] + nums[r] > 0) r--;
                else if (nums[i] + nums[l] + nums[r] < 0) l++;
                else {
                    res.push_back({nums[i], nums[l], nums[r]});
                    // 去重逻辑应该放在找到一个三元组之后,对r和l去重
                    //此处为何先进行r的去重,是因为即使数组中的数全相同,r也能最后停在l上
                    //即使后面再进行一次r--,r也能停到i上而不会溢出,反之若l在先去重,则后面收缩时会溢出
                    while (l < r && nums[r] == nums[r - 1]) r--;
                    while (l < r && nums[l] == nums[l + 1]) l++;

                    //再收缩一次才能到不同数的位置上
                    //这里注意是r--在前
                    r--;
                    l++;
                }
            }
            
        }
        return res;
    }
};

LeetCode 18.四数之和

链接18. 四数之和

思路

双指针:

  • 本题与上一题三数之和解法相似,区别在于上题使用一个for循环,将三个数中的一个设为固定值,使i为固定值;而本题使用双for,将四个数中的两个设为固定值,之后多数之和都是此思路,而双指针的作用在此处就很明显了,相比于暴力,可以简单一个数量级。
  • 其实如果完全吃透了上一题,会发现本题整体部分与上题区别不大,只是注意第二个for仍然要进行剪枝,去重操作
  • 以及注意个别例子超出int型最大值,使用long即可

:双指针法将时间复杂度:O(n^t)的解法优化为 O(n^(t-1)的解法。也就是降一个数量级

完整C++代码如下

//时间复杂度: O(n^3)
//空间复杂度: O(1)
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
		vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();i++){
            //剪枝,注意要使得nums[i]>=0,可能存在[-2,-1,0,0],target为-3这类情况
            //究其原因是target的值是否为正或负是不确定的,上题和为0是固定的
            //此处采用break是为了避免函数有多个出口,统一结束时返回
            if ( nums[i] > target && nums[i] >= 0 ){
                break;
            }
            //去重
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for(int j=i+1;j<nums.size();j++){
                //再次剪枝
                if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0){
                    break;
                }
                //再次去重
                if(j > i+1 && nums[j] == nums[j-1]){
                    continue;
                }
                int l=j+1;
            	int r = nums.size() - 1;
            	while (l < r) {
					//存在例子使得四数相加会溢出int的最大值
                    if ((long)nums[i] + nums[j] + nums[l] + nums[r] > target) r--;
                    else if ((long)nums[i] + nums[j] + nums[l] + nums[r] < target) l++;
                    else {
                        res.push_back({nums[i] , nums[j] , nums[l], nums[r]}); 
                        while (l < r && nums[r] == nums[r - 1]) r--;
                        while (l < r && nums[l] == nums[l + 1]) l++;
                        r--;
                        l++;
                    }    
            	}
            }
        }
        return res;
    }
};


  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值