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

454. 四数相加 II

题目链接:LeetCode454. 四数相加 II

文章讲解:代码随想录—454.四数相加 II

思路:

本题和两数之和很像,都是先建立哈希表,再在哈希表中寻找目标值减部分元素值的结果是否存在,存在的即是想要的结果。

两数之和只有一种答案,但需要返回结果下标,因此建立的哈希表需要储存数值和下标两种数据;本题中需要记录所有可能的情况,因此两两记录,把其中两数之和的所有情况先记录下来,顺便记录一下得到的结果次数,以便统计所有情况。

不需要排序、不需要考虑重复元素情况、记录两种数据,是 unordered_map 的使用情况。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map;
        for (int a : nums1) {
            for (int b : nums2) {
                map[a + b] ++;
            }
        }

        int count = 0;
        for (int c : nums3) {
            for (int d : nums4) {
                if (map.find(0 - c - d) != map.end()) count += map[0 - c - d];
            }
        }
        return count;
    }
};

时间复杂度:O(n ^ 2)

空间复杂度:O(n ^ 2),最坏情况下A和B的值各不相同,相加产生的数字个数为 n^2

383. 赎金信

题目链接:LeetCode383. 赎金信

文章讲解:代码随想录—383. 赎金信

思路:

首先只使用小写字母,一共只有26个元素,因此首先考虑可以使用数组存储字母元素。

magazine 中每个字母只会使用一次,因此数组可以记录 magazine 中字母个数,当 ransomNote 中出现字母时,数组中对应字母个数就减1,如果数组内有元素为负数,就返回 false,否则是 true

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

时间复杂度:O(n)

空间复杂度:O(1)

15. 三数之和

题目链接:LeetCode15. 三数之和

文章讲解:代码随想录—15. 三数之和

思路:

本题看似很像之前的两数之和、四数之和II,但比两数之和复杂在本题不止有一种答案,比四数之和II复杂在是要在一个数组(而非三个数组)中找到符合题意的元素,且不能重复利用。

因此如果使用哈希表,虽然也可以解决,但其中去重的步骤会很麻烦,空间复杂度也会很高(O(n))。

本题在排序后可以使用双指针算法,固定一个指针后,设定左右两个可以移动的指针,当三数之和太大时,把右侧指针左移,使其变小;三数之和太小时,把指针右移,是其变大,可以很好地解决问题。

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

时间复杂度:O(n ^ 2)

空间复杂度:O(1)

遇到的问题:

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

这一行不能写成

if (nums[i] == nums[i + 1]) continue;

会少考虑值相同元素不同的情况,可以实际验证一下

18. 四数之和

题目链接:LeetCode18. 四数之和

文章讲解:代码随想录—18. 四数之和

思路:

和三数之和思路完全相同,只需要注意去重和剪枝步骤即可

这两个题利用双指针算法会比普通暴力算法少一次循环,在五数之和、六数之和等等中也适用

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

时间复杂度:O(n ^ 3)

空间复杂度:O(1)

总结:

在做过的加和题中,三数之和和四数之和都用到的双指针算法,因为他们都要求输出所有符合题意的数据、不允许使用重复、答案可能存在很多种、且都在一个数列中寻找。而二数之和虽然也在一个数组中寻找,但答案只有一种,因此用哈希表就可以容易的解决问题。四数之和II中是在四个数组里面寻找符合题意的四个元素,因此不需要去重等麻烦的操作,哈希表可以解决。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值