代码随想录Day7 | 454.四数相加II 、383. 赎金信 、15. 三数之和 、18. 四数之和

454.四数相加II 

这道题看到第一时间的想法有些模糊,仔细阅读题目,发现不需要把具体的索引返回,所以考虑可以使用哈希表来解决,把第一个和第二个数组的和存到map中,同时把次数也统计到;然后在把另外两个数组各个索引求和sum,查找-sum是否在map中即可,这样相比暴力求解法,时间复杂度从O(n^{^{4}})降低为O(n^{_{2}}),详细代码如下:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> map1;
        int result = 0;
        for(int i=0;i<nums1.size();i++)
        {
            for(int j=0;j<nums2.size();j++)
            {
                ;
                int tmp = nums1[i]+nums2[j];
                if(map1.find(tmp)!=map1.end()) map1[tmp]++;
                else map1[tmp]=1;
            }
        }
        for(int i=0;i<nums3.size();i++)
        {
            for(int j=0;j<nums4.size();j++)
            {
                int tmp = -(nums3[i]+nums4[j]);
                if(map1.find(tmp)!=map1.end()) result+=map1[tmp];
            }
        }
        return result;

    }
};

在做这道题目的时候遇到了leetcode的一个错误:

==20==ERROR: AddressSanitizer: heap-buffer-overflow on address xxx

查资料发现这个bug的原因是数组越界,最终排查到为写双层嵌套循环时候j<num2.size()这一条件写成了i<num2.size(),后续如果再遇到这种错误可以考虑边界条件写错的可能性。

在看完代码随想录后,还学到了一个map的语法知识,即当map中没有key值时不用特殊处理,直接++即可

        for(int i=0;i<nums1.size();i++)
        {
            for(int j=0;j<nums2.size();j++)
            {
                ;
                int tmp = nums1[i]+nums2[j];
                map1[tmp]++;

            }
        }

 383. 赎金信  

这道题目和异位词有相似之处,不同的只需判断A是否由B组成即可,把B做成哈希表,然后遍历A的字符查找是否在B中有即可。

此外学习了C++ 11语法知识之for循环:

详细代码如下:

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map <char, int> map1;
        for(char s:magazine)
        {
            map1[s]++;
        }
        for(char s:ransomNote)
        {
            if(map1.find(s)==map1.end()||map1[s]<=0) return false;
            map1[s]--;
        }
        return true;

    }
};

代码随想录建议用数组来当字典更加有效,原因是:其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

 15. 三数之和 

这道题目十分经典,使用双指针来减少一层循环,使得三数之和复杂度变为O(n^{2}),主要有几个去重逻辑需要重点看,我在这道题目中犯了一个错误是:我考虑到找到满足条件的结果需要分别去重左边界和有边界,但是我却忘记了在此之外,左右边界要同时收缩去找下一个满足条件的元组,一开始我忘记同时收缩i和j导致进入了死循环,超时。看了代码随想录知道问题所在后修改进行提交,AC题目:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for(int k=0;k<nums.size()-2;k++)
        {
            if(nums[k]>0) break;
            if(k>0&&nums[k]==nums[k-1]) continue;
            int i=k+1, j=nums.size()-1;
            while(i<j)
            {
                if(nums[k]+nums[i]+nums[j]==0)
                {
                    res.push_back({nums[k],nums[i], nums[j]});
                    while(i+1<j&&nums[i+1]==nums[i]) i++;
                    while(j-1>i&&nums[j-1]==nums[j]) j--;
                    //找到答案要同时收缩,不然就陷入死循环了
                    i++;
                    j--;
                }
                else if(nums[k]+nums[i]+nums[j]<0) i++;
                else j--;
            }
        }
        return res;

    }
};

 18. 四数之和  

这道题目和三数之和类似,但是这道题需要注意的点有两个:

1. 因为target变成了随机数而不是零,所以不可以用nums[k]>target的逻辑去剪枝,如target为负数的话,和比任意一个数更小;在这种情况下,要剪枝必须得保证target>=0,所以两个剪枝可以写为:

if (nums[m]>target&&target>=0)
if (nums[k] + nums[m] > target && target>=0)
 

2. 这道题需要考虑边界问题,当每个数都很大的时候,四个数相加会大于int的最大值,此时需要将数字强转成long型在进行求和,否则会越界,后续遇到类似的边界也需要考虑该问题。

详细代码如下:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        if(nums.size()<4) return res;
        for(int m=0;m<nums.size()-3;m++)
        {
            //if(nums[m]>target) break;
            if(m>0&&nums[m-1]==nums[m]) continue;
            for(int k=m+1;k<nums.size()-2;k++)
            {
                //if((nums[m]+nums[k])>target) break;
                if(k>m+1&&nums[k]==nums[k-1]) continue;
                int i=k+1,j=nums.size()-1;
                while(i<j)
                {
                    //此处要主要数据溢出问题,先转换类型
                    long tmp = (long)nums[m]+nums[k]+nums[i]+nums[j];
                    if(tmp<target) i++;
                    else if(tmp>target) j--;
                    else
                    {
                        res.push_back({nums[m],nums[k],nums[i],nums[j]});
                        while(i+1<j&&nums[i+1]==nums[i]) i++;
                        while(j-1>i&&nums[j-1]==nums[j]) j--;
                        i++;
                        j--;
                    }
                }
            }
        }
        return res;

    }
};

今日学习两个小时~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值