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

454.四数相加||

链接:LeetCode454.四数相加||
思路:

  1. 暴力法。四层for循环。时间复杂度为O(n4)
  2. 哈希表法。每两个数组为一组,将每个相对位置相加的结果存储在unordered_map中。最后用一层for循环寻找满足条件的四个数字。时间复杂度为O(n2)
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        //ma1,ma2用来记录和数值以及对应的次数
        unordered_map<int,int> ma1,ma2;
        for(int i=0;i<nums1.size();++i){
            for(int j=0;j<nums2.size();++j){
                ++ma1[nums1[i]+nums2[j]];
            }
        }
        for(int i=0;i<nums3.size();++i){
            for(int j=0;j<nums4.size();++j){
                ++ma2[nums3[i]+nums4[j]];
            }
        }
        int ans = 0 ;
        for(auto it = ma1.begin();it!=ma1.end();++it) {
            ans += it->second * ma2[-it->first];
        }
        return ans;

    }

};

383.赎金信

链接:LeetCode383.赎金信
思路:

  • 将magazine中出现的字符以及对应出现的次数记录在哈希表中。将ransomNote中出现的字符也记录在哈希表中。然后两个哈希表逐一比较。虽然时间复杂度控制在O(n)。但是做了三次循环。其实可以将其中一个循环优化掉。之将magazine的字符记录在哈希表中。详解请见代码。
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int ham[26] = {0};
        for(const char &c:magazine) ++ham[c-'a'];
        for(const char &c:ransomNote){
            if(--ham[c-'a'] < 0) return false;
        }
        return true;
    }
};

15.三数之和

链接:LeetCode15.三数之和
思路:

  • 暴力解法。三层循环。
  • 由两数之和得到的启示,可以利用哈希表进行优化。变成两层for循环。但是需要先用set去重,之后将去重后的答案变成vector返回。做了很多不必要的操作。
  • 用双指针进行优化。时间复杂度为O(n2)

双指针

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();++i){
            if(nums[i]>0) return ans;
            //去重
            if(i>0 && nums[i-1] == nums[i]) continue;
            int val = -nums[i];
            int l=i+1,r=nums.size()-1;
            while(l<r){
                if(nums[l]+nums[r]==val){
                    ans.emplace_back(vector<int>{nums[i],nums[l],nums[r]});
                    //去重
                    while(l<r&&nums[l+1]==nums[l]) ++l;
                    while(l<r&&nums[r-1]==nums[r]) --r;
                    ++l,--r;//向前走
                }else if(nums[l]+nums[r]>val) --r;
                else ++l;
            }
        }
        return ans;
    }
};

排序是为了更好的去重

18.四数之和

链接:LeetCode18.四数之和

双指针

时间复杂度为O(n3)

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> ans;
        sort(nums.begin(),nums.end());
        for(int i=0;i<nums.size();++i){
            if(nums[i]>target&&nums[i]>=0) break;
            if(i>0&&nums[i-1]==nums[i]) 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-1]==nums[j]) continue;
                long  val = (long)target-nums[i]-nums[j];
                int l=j+1,r=nums.size()-1;
                while(l<r){
                    if((long)nums[l]+nums[r]==val){
                        ans.emplace_back(vector<int>{nums[i],nums[j],nums[l],nums[r]});
                        while(l<r&&nums[l+1]==nums[l]) ++l;
                        while(l<r&&nums[r-1]==nums[r]) --r;
                        ++l,--r;
                    }else if((long)nums[l]+nums[r]>val) --r;
                    else ++l; 
                }
            }
        }
        return ans;
    }
};

需要注意的地方:

  1. 第一层循环剪枝:if(nums[i]>target&&nums[i]>=0)。第一次编写的时候直接写成了if(nums[i]>target)这样写是不对的,因为此时不知道nums[i]与0的关系,如果nums[i]<0那么target加上nums[i]之后的元素可能会变小,所以还需要判断nums[i]与0的关系。二层内循环的剪枝也是如此。
  2. 对于本题的数据相加或者相减可能会产生溢出。所以需要将数据强制类型转换为数据范围更大的long类型。
  3. 内存循环剪枝的时候“if(nums[i]+nums[j]>target && nums[i]+nums[j]>=0) break;”应该使用break退出当前的for循环。而不是return推出整个循环直接结束程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值