代码随想录算法训练营第七天 |LeetCode454四数相加Ⅱ 383赎金信 15三数之和 18四数之和

文章介绍了使用优化算法解决LeetCode上的几道编程题,包括通过分组求和降低时间复杂度解决四数相加问题,利用字符计数判断赎金信可行性,以及应用排序和双指针技术高效找出数组中三数和、四数和为零的组合。
摘要由CSDN通过智能技术生成

1.四数相加Ⅱ(力扣

        暴力解法四层for循环,复杂度太高辣,直接给你来个超出时间限制,所以要想办法降低时间复杂度。

        4个不行,那可以抓两个,把四个数组分成两组,对第一组进行求和,用一个unordered_map来储存数及它的出现次数。用两层for循环对另外两个数组进行遍历,如果在map中找到了-(c+d)则证明组合是可行的,但是由于出现的数目可能不为1,不能直接++,需要加上map[-(c+d)]的值才是正确答案。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>init_map;
        for(int a:nums1)
        {
            for(int b:nums2)
            {
                init_map[a+b]++;
            }
        }
        int count=0;
        for(int c:nums3)
        {
            for(int d:nums4)
            {
                if(init_map.find(-(c+d))!=init_map.end())
                {
                    count+=init_map[-(c+d)];
                }
            }
        }
        return count;
    }
};

2.赎金信(力扣

对于给定的字符串magazine,里面的字符只能被ransomNote使用一次,很容易想到的就是遍历magazine,把字符串的数目进行统计用一个数组进行储存,在遍历ransomNote,出现的字符直接在数组的对应位置进行取出,如果某一个数组位置的数小于0,就证明magazine'已经满足不了ransomNote的需求了,直接return false。

P.S 在开始之前可以先比较两个字符串的长度,如果magazine更短直接return false。

bool canConstruct(char * ransomNote, char * magazine){
    int result[26]={0};
    int length=strlen(magazine);
    for(int i=0;i<length;i++)
    {
        result[magazine[i]-'a']++;
    }
    for(int i=0;i<strlen(ransomNote);i++)
    {
        result[ransomNote[i]-'a']--;
        if(result[ransomNote[i]-'a']<0)
        {
            return false;
        }
    }
    return true;
}

3.三数之和(力扣

相比起前两道这个题会更加复杂一点,而复杂的地方在于去重+剪枝。先将数组进行排序,利用双指针进行遍历,由于要求三数之和为0的组合,当第一个数字为正数的时候,后面的其实已经不用判断了,三数之和只能大于0,此过程称为剪枝;而数组中是极有可能出现相同元素的,当题目要求不能有重复的三元组出现,那么在每一次判断前都要进行去重:

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

为什么这里是nums[i]和nums[i-1]而不是nums[i]与nums[i+1]捏?如果为后者,在双指针赋初值的时候left=i+1,这个判断条件只能限制三元组里不能有重复的元素而不是限制重复的三元组出现。

用双指针对三数之和进行判断。left指向当前位置的下一个元素,right指向最后一个元素,如果和大于0,则证明过大,所有将右指针向左移动一位(right--);如果和小于0,则证明过小,将左指针向右移动一位(left++)。如果刚好为0,那么对其进行储存,接下来去重操作。(双指针有效降低复杂度)

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

                    while(left<right&&nums[right]==nums[right-1])
                    {
                        right--;
                    }
                    while(left<right&&nums[left]==nums[left+1])
                    {
                        left++;
                    }
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

4.四数之和(力扣

四数之和本质上就只是在三数之和的基础上加了一层for循环,四个数->固定一个,移动第二个的时候又固定,剩下两个数变成双指针移动的区间。值得注意的是,新增的一层for循环依然需要增加剪枝和去重操作,但是条件与三数之和有所不同,给定的target不再是0,所以不能仅仅依据排序后的数组出现nums[k]>target就直接宣布死刑,当出现[-3,-3,-2,0]和target=-8的时候这么做直接G。所以需要加上一点点条件,对于最外层的for循环nums[k]>target>=0,对于第二层循环nums[i]+nums[k]>target>=0,就能够顺利完成剪枝操作,而去重操作与第三题三数之和相同,不再进行赘述。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>>result;
        sort(nums.begin(),nums.end());
        for(int k=0;k<nums.size();k++)
        {
            if(nums[k]>0&&nums[k]>target)
            {
                break;
            }
            if(k>=1&&nums[k]==nums[k-1])
            {
                continue;
            }
            for(int i=k+1;i<nums.size();i++)
            {
                if((long)nums[i]+(long)nums[k]>target&&nums[i]+nums[k]>=0)
                {
                    break;
                }
                if(i>k+1&&nums[i]==nums[i-1])
                {
                    continue;
                }
                int left=i+1;
                int right=nums.size()-1;
                while(left<right)
                {
                    if((long)nums[k]+(long)nums[i]+nums[left]+nums[right]>target)
                    {
                        right--;
                    }
                    else if((long)nums[k]+(long)nums[i]+nums[left]+nums[right]<target)
                    {
                        left++;
                    }
                    else
                    {
                        result.push_back(vector<int>{nums[k],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;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值