代码随想录算法训练营第六天|454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

代码随想录算法训练营第六天|454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

第454题.四数相加II

题目链接:https://leetcode.cn/problems/4sum-ii/

看到这题的时候,虽说知道要用哈希,但是却无从下手…,看完卡哥的思路分析之后,写出的代码如下(本题的暴力是O(n4),通过哈希改善之后是O(n2)):

 int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
                unordered_map<int,int> map;
                int sum1=0,sum2=0,count=0;
                for(int i=0;i<nums1.size();i++)
                {
                    for(int j=0;j<nums2.size();j++)
                    {
                        sum1=nums1[i]+nums2[j];
                        if(map.find(sum1)==map.end())
                            map.insert(pair<int,int>(sum1,1));
                        else
                            map.find(sum1)->second++;
                    }
                }
                for(int i=0;i<nums3.size();i++)
                {
                    for(int j=0;j<nums4.size();j++)
                    {
                        sum2=nums3[i]+nums4[j];
                        if(map.find(-sum2)!=map.end())
                        {
                            count+=map.find(-sum2)->second;
                        }
                    }
                }
                return count;
            }

tips:一眼没有思路时,可以先想一想题目的暴力解法,想出来之后在对它的时间复杂度进行改善。还有不要轻易被题目带偏,按自己的理解和思路来。

如本题要的时四元组的个数,我们未必就要真的直接去求四元组,真的把i、j、k、l各自求出来,我们只需要抓住问题的本质就可——这样的组合的个数!想办法把组合的个数求出来就可以了!

总体思路:减少数组个数:把1、2看成一个整体并存入map,把3、4看成一个整体并放到map中来检验是否存在。注意,当存在sum1=-sum2时,四元组的个数就是map中对应的value

无重复元素(想去重)用unordered_set , 允许重复元素存在,用multiset,想要精确的知道元素重复的数量用unordered_map , key存储元素,value存储元素的数量

383. 赎金信

题目链接:https://leetcode.cn/problems/ransom-note/description/

这道题比较简单,相当于是242的一个拓展,思路与242相同。具体代码如下:

  bool canConstruct(string ransomNote, string magazine) {
                    int hash[26]={0};
                    for(int i=0;i<ransomNote.size();i++)
                    {
                        hash[ransomNote[i]-'a']++;
                    }
                    for(int i=0;i<magazine.size();i++)
                    {
                        hash[magazine[i]-'a']--;
                    }
                    for(int i=0;i<26;i++)
                    {
                        if(hash[i]>0)
                        return false;
                    }
                    return true;
                }

15. 三数之和

题目链接:https://leetcode.cn/problems/3sum/description/

双指针法

第一眼看到题目,感觉跟454很想,于是还想继续用哈希法,结果发现“下标互不相等”这个条件满足不了。看完了卡哥的视频之后,用双指针法实现的代码如下:

vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> result;
        int left=0,right=0;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]>0)
                return result;
            if(i>0&&nums[i]==nums[i-1])
                continue;
            left=i+1;
            right=nums.size()-1;
            while(left<right)
            {
                if(nums[i]+nums[left]+nums[right]>0)
                    right--;
                else if(nums[i]+nums[left]+nums[right]<0)
                    left++;
                else 
                {
                    result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    while(left<right&&nums[left]==nums[left+1])
                        left++;
                    while(left<right&&nums[right]==nums[right-1])
                        right--;
                    left++;
                    right--;
                }
            }
        }
        return result;
    }

补充知识(STL):

  1. sort(nums.begin(),nums.end()); 将始末位置输入sort()函数中,即可对vector内的元素进行排序(升序)

  2. result.push_back(vector{nums[i],nums[left],nums[right]}); 用push_back来增加或插入元素于vector中

  3. 二维数组的格式:vector<vector> result;

插入的格式:push_back(vector{nums[i],nums[left],nums[right]}

总体思路:因为只需要元素的大小,不需要其下标,所以首先对数组进行排序(升序)然后,用i指针来循环遍历,再设置left和right两个指针,分别指向i之后的序列的首尾两端。其次,由于升序排列,当nums[i]+nums[left]+nums[right]**>**0时,right指针左移;left指针同理。最后,当等于0的时候,将此时的3个元素以数组的形式存在二维数组中。

==思路有点复杂,但更易错的却是去重操作

  1. 对i指针去重:nums[i]==nums[i-1]时要跳过当前i。注意!不能是nums[i]=nums[i+1]!因为i+1在i的后面,left指针可能会指向i+1,这样就会遗漏
  2. 对left、right指针去重(只有当nums[i]+nums[left]+nums[right]=0时才需要讨论):以left为例,while(left<right&&nums[left]==nums[left+1]),当left当前位置的值与下一个位置相等时,left要多跳一个位置,由此循环(我错就是错在用if,而不是用while)。

18. 四数之和

题目链接:https://leetcode.cn/problems/4sum/

双指针法

看视频之前我估计与三数之和比较类似,可能需要在前面再来一个for循环。结果跟我想的差不多,后面的left、right指针的操作基本相同,但是前面两个for循环的剪枝、去重的细节还时出乎我的意料的,具体代码如下:

vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        int left=0,right=0;
        for(int k=0;k<nums.size();k++)
        {
            if(nums[k]>target&&nums[k]>=0)
                break;
            if(k>0&&nums[k]==nums[k-1])
                continue;
            for(int i=k+1;i<nums.size();i++)
            {
                if(nums[k]+nums[i]>target&&nums[k]+nums[i]>=0)
                    break;
                if(i>k+1&&nums[i]==nums[i-1])
                    continue;
                left=i+1;
                right=nums.size()-1;
                while(left<right)
                {
                if((long)nums[k]+nums[i]+nums[left]+nums[right]>target)
                right--;
                else if((long)nums[k]+nums[i]+nums[left]+nums[right]<target)
                left++;
                else
                    {
                    result.push_back(vector<int>{nums[k],nums[i],nums[left],nums[right]});
                    while(nums[right]==nums[right-1]&&left<right)
                    right--;
                    while(nums[left]==nums[left+1]&&left<right)
                    left++;
                    right--;
                    left++;
                    }
                }
            }
        }
        return result;
    }

剪枝、去重:

  1. 对 k指针:
    • 剪枝:if(nums[k]>target&&nums[k]>=0),此时后面就不需在讨论了,一定大于target。要注意的是:nums[k]是正数时才能满足条件,因为正正相加越来越大,负负相加越来越小
    • 去重:if(k>0&&nums[k]==nums[k-1])
  2. 对 i指针:
    • 剪枝:(nums[k]+nums[i]>target&&nums[k]+nums[i]>=0,原理与k的类似,但是这是要将nums[k]和nums[i]同时算进去。
    • 去重:if(i>k+1&&nums[i]==nums[i-1]),要注意的时 i 是在 k 之后的,所以 i 要大于 k+1

另外,题目中nums[i]可以取到10亿,而int最大可以取到21亿,当4个nums[i]相加时,是有可能溢出的,所以要将int转换位long

  • 12
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值