Bootstrap

代码随想录算法训练营第六天|LeetCode454.四数相加II LeetCode 383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和

代码随想录算法训练营

Day6 代码随想录算法训练营第六天 |LeetCode454.四数相加II LeetCode 383. 赎金信 LeetCode15. 三数之和 LeetCode18. 四数之和



前言

今天后两道题由于去重的复杂性不能使用哈希法
(如果把数组放进map,再用.find判断是否出现,会超时)

LeetCode454.四数相加II

讲解文档

LeetCode383. 赎金信

讲解文档

LeetCode15. 三数之和

讲解文档

LeetCode18. 四数之和

讲解文档


一、LeetCode454.四数相加II

1.题目链接

LeetCode454.四数相加II

2.思路

(1)先用两次for循环遍历A和B,求A+B并作为map的键,出现次数作为map的值
(2)再用两层for循环遍历C和D,判断0-C-D是否为map的键,如果是,答案加上对应的值

3.题解

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        int count=0;
        int n=nums1.size();
        unordered_map<int,int> m;
        
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                m[nums1[i]+nums2[j]]++;
            }
        }
        for(int i=0;i<n;i++)
        {
            for(int j=0;j<n;j++)
            {
                auto iter=m.find(0-nums3[i]-nums4[j]);
                if(iter!=m.end())
                    count+=iter->second;
            }
        }
        return count;
    }
};

二、LeetCode383. 赎金信

1.题目链接

LeetCode383. 赎金信

2.思路

(1)思路与LeetCode242有效的字母异位词相似,字母作为哈希表的索引
(2)ransomNote出现字母则对应元素-1,magazine出现对应字母则对应元素+1
(3)如果最后哈希表里有负数,那么说明ransomNote有字母没有在magazine中出现足够多次

3.题解

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int a[26]={0};
        for(int i=0;i<ransomNote.size();i++ )
        {
            a[ransomNote[i]-'a']--;
        }
        for(int i=0;i<magazine.size();i++)
        {
            a[magazine[i]-'a']++;
        }
        for(int i=0;i<26;i++)
        {
            if(a[i]<0)return 0;
        }
        return 1;
    }
};

三、LeetCode15. 三数之和

1.题目链接

LeetCode15. 三数之和

2.思路

(1)为什么不适合哈希法:把符合条件的三元组放进vector中,然后再去重容易超时
(2)数组排序
(3)for循环遍历找答案里最小的元素A,双指针遍历找另外两个元素BC
和大于0则右指针向左移动,和小于0则左指针向右移动,和为0记录答案,左右指针同时移动(若下一个B或者C和现在的一样,就是重复;若下一个B、C和现在不一样,那么应该移动指针)
(4)剪枝:
答案中最小元素大于0,则跳出循环
(5)去重:
1)A去重:

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

如果某个元素和上一个元素相等,则它已经被讨论过了
为什么不用nums[k] == nums[k +1]判定:这种会舍弃所有有重复元素的答案 e.g nums={0,0,0}
2)B去重

while (i < j && nums[i] == nums[i + 1])
                        i++;

nums[i]已经被记录了,如果下一个还和它一样,那么跳过
3)C去重

while (i < j && nums[j] == nums[j - 1])
                        j--;

nums[j]已经被记录了,如果下一个还和它一样,那么跳过

3.题解

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans = {};
        int n = nums.size();
        sort(nums.begin(), nums.end());

        for (int k = 0; k < n; k++) {
            if (k > 0 && (nums[k] == nums[k - 1]))
                continue;
            if (nums[k] > 0)
                break;
            int i = k + 1;
            int j = n - 1;
            while (i < j) {

                if (nums[i] + nums[j] + nums[k] < 0)
                    i++;
                else if (nums[i] + nums[j] + nums[k] > 0)
                    j--;
                else {

                    ans.push_back(vector<int>{nums[k], nums[i], nums[j]});
                    while (i < j && nums[i] == nums[i + 1])
                        i++;
                    while (i < j && nums[j] == nums[j - 1])
                        j--;
                    i++;
                    j--;
                }
            }
        }
        return ans;
    }
};

四、LeetCode

1.题目链接

LeetCode

2.思路

(1)在三数之和的基础上加一层for循环
(2)剪枝
1)不能直接用nums[i]>target来剪枝,因为元素可能是负数,相加变小
2)A和B剪枝时要确保元素都是正的
A剪枝

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

B剪枝

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

(3)去重方法和三数之和一样

3.题解

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

            for (int j = i + 1; j < n; j++) {
                if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0)
                    break;
                if (j > i + 1 && nums[j] == nums[j - 1])
                    continue;
                int l = j + 1;
                int r = n - 1;
                while (l < r) {
                    long s = (long)nums[i] + nums[j] + nums[l] + nums[r];
                    if (s < target)
                        l++;
                    else if (s > target)
                        r--;
                    else {
                        res.push_back({nums[i], nums[j], nums[l], nums[r]});
                        while (l < r && nums[l] == nums[l + 1])
                            l++;
                        while (l < r && nums[r] == nums[r - 1])
                            r--;
                        l++;
                        r--;
                    }
                }
            }
        }
        return res;
    }
};

总结

不用考虑重复问题,只考虑是否出现过,用哈希法简单
如果要考虑去重,用双指针更简单

;