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

今日任务

454. 四数相加 II
383. 赎金信
15. 三数之和
18. 四数之和

454. 四数相加 II

主要思路:数据两两为一组计算

一个unordered_map,key放a和b两数之和,value 放a和b两数之和的负数(0-sum)出现的次数

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> pairSum; //key = sum, val = 等于sum的个数
        int len = nums1.size(), res = 0, tmp;
        // nums1 与 nums2 一组
        for (int i = 0; i < len; ++i) {
            for (int j = 0; j < len; ++j) {
                tmp = -(nums1[i] + nums2[j]);
                if (!pairSum.count(tmp)) pairSum[tmp] = 1;
                else ++pairSum[tmp];
            }
        }

        for (auto c : nums3) {
            for (auto d : nums4) {
                tmp = c + d;
                if (pairSum.count(tmp)) res += pairSum[tmp];
            }
        }
        
        return res;
    }
};

383. 赎金信

主要思路:先用unordered_map计算存储每个字母在magazine中出现次数,再遍历ransomNote看其字母是否都可以被magazine覆盖

第二次循环时如果c被check出来,那么其对应的出现次数-1,为0时将其从unordered_map中删掉

class Solution {
public:
    // void toLowwerCase()
    bool canConstruct(string ransomNote, string magazine) {
        // transform(ransomNote.begin(), ransomNote.end(), ransomNote.begin(), ::tolowwer);
        unordered_map<int, int> magCnt;
        for (auto c : magazine) {
            if (magCnt.count(c)) magCnt[c]++;
            else magCnt[c] = 1;
        }
        for (auto c : ransomNote) {
            if (!magCnt.count(c)) return false;
            magCnt[c]--;
            if (magCnt[c] == 0) magCnt.erase(c);
        }
        return true;
    }
};

15. 三数之和

主要思路:先对数组排序,前一个数直接循环遍历,后两个数用指针

先剪枝再去重
然后记得这个题是返回的是元素本身而不是它们的下标

class Solution {
public:
// 记住,结果返回的是数字不是下标,别看错题
// 使用双指针, 设三个数分别位 a, b, c
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end()); // 原容器的顺序改变
        int len = nums.size(), left, right, sum;
        vector<vector<int>> res;
        for (int i = 0; i < len; ++i) {
            if (nums[i] > 0) return res; // 如果第一个元素已经大于0,那么不可能成为三元组,直接返回结果
            // 首先对a去重
            if (i > 0 && nums[i] == nums[i - 1]) continue; 
            left = i + 1, right = len - 1;
            while (left < right) {
                sum = nums[i] + nums[left] + nums[right];
                if (sum == 0) {
                    res.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 对b和c去重,此时应当在一个三元组第一次放入结果之后对b, c去重
                    while (left < right && nums[left] == nums[left + 1]) left++;
                    while (left > right && nums[right] == nums[right - 1]) right--;
                    // 找到答案时两个指针同时收缩
                    left++, right--;
                } else if (sum > 0) {
                    right--;
                } else {
                    left++;
                }
            }
        }
        return res;
    }
};

18. 四数之和

主要思路:先对数组排序,前两个数直接用循环遍历,后两个数用双指针转化为O(n)

前两个循环可以先进行剪枝(也就是当数据已经来到正数,且前面的和的值已经大于0就不再往后计算)
然后记得对每个数进行去重

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        int len = nums.size();
        for (int a = 0; a < len; ++a) {
            if (nums[a] > target && nums[a] > 0) break;

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

            for (int b = a + 1; b < len; ++b) {
                if (nums[a] + nums[b] > target && nums[a] + nums[b] >= 0) break;

                // 给nums[b]去重
                // 只需要单个位置的数前面相同时只出现一次即可,所以nums[a] == nums[b]是可以的,所以此处b > a + 1
                if (b > a + 1 && nums[b] == nums[b - 1]) continue; 
               
                int left = b + 1, right = len - 1;
                while (left < right) {
                    long sum = (long)nums[a] + nums[b] + nums[left] + nums[right];
                    if (sum > target) right--;
                    else if (sum < target) left++;
                    else {
                        res.push_back(vector<int>{nums[a], nums[b], 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 res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值