记录算法Day6

代码随想录第六天|Leetcode 454 四数相加II,383 赎金信,(*)15 三数之和,(*)18 四数之和

Leecode 454.四数相加II

题目链接 454 四数相加II
文章链接 https://www.programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html
视频链接 https://www.bilibili.com/video/BV1Md4y1Q7Yh

思路

可以把四数相加变成两数相加,问题就转换为和昨天差不多的两数相加。先挑任意两个数组
,计算两个数组对应的数的和,作为键存储,值为出现次数,剩下两个数组同理,判断map[target-和]是否出现过即可

代码

class Solution {
//四数之和可以转换成两数之和,分组

public:

    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {

        unordered_map<int,int> map;

        int count = 0;

        for(int a:nums1){

            for(int b:nums2){

                map[a+b]++;

            }

        }//将任意两个数组每个值分别相加的和作为key存入map,value为出现的次数;

        for(int c:nums3){

            for(int d:nums4){

                auto it = map.find(0-(c+d));

                if(it!=map.end()){

                    count+=map[0-(c+d)];

                }else{

                    continue;

                }

            }

        }//计算剩下两个数组每个值分别相加的和,判断target-和是否已经出现,出现则计算出现次数

        return count;

    }

};

Leetcode 383 赎金信

题目链接 383 赎金信
文章链接 https://www.programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html

思路

LCR 032. 有效的字母异位词相似,判断magazine中的字符能不能构成ransomNote,就是看对应的字符够不够即可,用哈希比较简单

class Solution {

public:

    bool canConstruct(string ransomNote, string magazine) {

        vector<int> record(26,0);

        for(char c:ransomNote){

            record[c-'a']++;//记录r中每个字符的出现次数

        }

        for(char c:magazine){

            record[c-'a']--;//如果magazine也出现和r中相同的字符,数目减1,如果数目够的话,最后的值应该是小于等于0的,否则数目不够不能构成

        }

        for(int i=0;i<26;i++){

            if(record[i]>=1){//大于等于一说明r中有的字符在m中数目不够,所以不能构成

                return false;

            }

        }

        return true;

    }

};

Leetcode 15 三数之和

题目链接 15 三数之和
文章链接 https://www.programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html
视频链接 https://www.bilibili.com/video/BV1GW4y127qo

思路

这道题和之前的四数之和II,[两数之和是有区别的,前面的题都是求下标,而且四数之和II不在同一个数组中,所以组合重复也没关系。而这道题就要考虑去重条件了,用哈希不太好做,且这道题目问的是组合的情况而不是下标,就说明数组可以排序,用双指针更快一些,下面的四数之和也是同理。

  • 过程:拿这个nums数组来举例,首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
  • 细节:去重 要想每个组合不重复,就需要对每个数进行去重处理
    • 第一个数的去重处理:nums[i-1]=nums[i],这里不能是nums[i+1]=nums[i](对应情况:{-4,-4,3})
    • 第二个数的去重处理:第二个数是由left决定的,所以为nums[left]=nums[left+1] left++
    • 第三个数的去重处理:nums[right]=nums[right-1] right–
class Solution {

public:

    vector<vector<int>> threeSum(vector<int>& nums) {

        //使用双指针的前提:数组可以排序

        vector<vector<int>> result;

        sort(nums.begin(),nums.end());

        int left=0,right=nums.size()-1;

        int flag = 0;

        for(int i=0;i<nums.size()-2;i++){
	       //剪枝
	       
	        if(nums[i]>0) return result;

            //去重第一个数

            if(i>=1&&nums[i]==nums[i-1]){

                continue;

            }

            left = i+1;

            right = nums.size()-1;

            //条件是大于,不能相等的原因是一个元素不能使用两次

            while(right>left){

                //和大了就让right向左移动

                if(nums[i]+nums[left]+nums[right]>0){

                    right--;

                //和小了就让left向右移动

                }else if(nums[i]+nums[left]+nums[right]<0){

                    left++;

                }else{

                    result.push_back({nums[i],nums[left],nums[right]});

                    //当找到三元组后,还需对剩下两个数去重

                    while(right>left&&nums[right]==nums[right-1]) right--;

                    while(right>left&&nums[left]==nums[left+1]) left++;

                    right--;

                    left++;

                }

            }

        }

        return result;

    }

};

本题收获

这道题想了很久一点思路没有,看了卡哥的题解才知道可以用双指针做,还是学死了,不会灵活使用之前学过的算法。看了题解之后去重条件理解了一段时间,之前我所想到的是暴力遍历去重,使用双指针并且对数组排序后去重就方便很多还是要多复习这道题

Leetcode 18 四数之和

题目链接 18. 四数之和
文章链接 https://www.programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html
视频链接 https://www.bilibili.com/video/BV1DS4y147US

思路

和三数之和的思路一模一样了,只不过这次相当于多了一层循环
15.三数之和 的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下标作为双指针,找到nums[i] + nums[left] + nums[right] == 0。
四数之和的双指针解法是两层for循环nums[i] + nums[j]为确定值,依然是循环内有left和right下标作为双指针,找出nums[i] + nums[j] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。

class Solution {

public:

    vector<vector<int>> fourSum(vector<int>& nums, int target) {

        vector<vector<int>> result;

        //对数组排序

        sort(nums.begin(),nums.end());

        int n = nums.size();

        int left,right=n-1;

        for(int i=0;i<n-3;i++){

            //如果排序之后nums[i]大于target并且nums[i]大于等于0,则目标值不可能存在,剪掉这条路径

            if(nums[i]>target&&nums[i]>=0) break;

            //nums[i]去重

            if(i>=1&&nums[i]==nums[i-1]) continue;

            for(int j=i+1;j<n-2;j++){

                //如果此时nums[i]+nums[j]大于target并且二者之和不小于0,则目标值不可能存在,剪掉这条路径

                if(nums[j]+nums[i]>target&&nums[j]+nums[i]>=0) break;

                //nums[j]去重

                if(j>=i+2&&nums[j]==nums[j-1]) continue;

                left = j+1;

                right = n-1;

                while(left<right){
                
					//这里必须转换一下数据类型,int可能不太够
                    if((long)nums[i]+nums[left]+nums[right]+nums[j]>target){

                        right--;

                    }else if((long)nums[i]+nums[left]+nums[right]+nums[j]<target){

                        left++;

                    }else{

                        result.push_back({nums[i],nums[left],nums[right],nums[j]});

                        while(left<right&&nums[left]==nums[left+1]) left++;

                        while(left<right&&nums[right]==nums[right-1]) right--;

                        left++;

                        right--;

                    }

                }

            }

        }

        return result;

    }

};
  • 18
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值