代码随想录训练营day07打卡

四数相加II

454. 四数相加 II

给你四个整数数组 nums1nums2nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

此题与两数相加类似,由于并不用输出四元组元素对应的下标,因此不需要用hashmap的value来存储下标值。如果我们用朴素算法求四数相加=0,那么时间复杂度则是O(n^4),为了降低复杂度,我们可以通过将nums1、nums2相加得到数字A的值,然后将nums3与nums4看成数字B的值。将A和B看成两数相加中的nums1和nums2,后续处理方法类似,通过将A存入mapA中,value值保存该key值的次数,然后遍历-nums3[k]-nums4[l](因为满足条件的四元组是nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0),如果(-nums3[k]-nums4[l])在mapA中,则count+=mapA[-nums3[k]-nums4[l]],由于得到A并存入mapA过程我们只遍历了nums1和nums2,时间复杂度为O(n^2),类似得到B时间复杂度也为O(n^2),判断-B是否在mapA中的复杂度为O(1),因此最后时间复杂度为O(n^2)。与朴素算法的O(n^4)相比下降了两个数量级。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> mapA;
        unordered_map<int, int> mapB;
        int count = 0;
        for (int i = 0; i < nums1.size(); i++)
        {
            for (int j = 0; j < nums2.size(); j++)
            {
                if (mapA.find(nums1[i] + nums2[j]) != mapA.end()) mapA[nums1[i] + nums2[j]]++;
                else mapA.insert(pair<int,int>(nums1[i] + nums2[j],1));
            }
        }
        for (int i = 0; i < nums3.size(); i++)
        {
            for (int j = 0; j < nums4.size(); j++)
            {
                if (mapA.find(-nums3[i] - nums4[j]) != mapA.end()) count+= mapA[-nums3[i] - nums4[j]];
            }
        }
        return count;
    }
};

赎金信

383. 赎金信

给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。

如果可以,返回 true ;否则返回 false 。

magazine 中的每个字符只能在 ransomNote 中使用一次。

此题与字母的同构异位词解法几乎相同,唯一不同的地方是同构异位词需要两个字符串的字母频率一致,而赎金信只需magazine的字母频率大于ransomNote。因此遍历完两个数组之后,只需判断hashmap的元素是否存在大于0的,如果存在,说明该字母magazine的频率小于于ransomNote,返回false。

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

三数之和

15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

此题的难点是需要判断的数字都处于一个数组中,去重比较麻烦,如果用hashmap实现比较复杂。因此通过双指针实现(每一个指针的位置不同),先将数组排序,方便后续操作,使用i从前往后遍历,然后每一次遍历开始,left指针指向i后面一个结点,right指针指向数组末尾。因为目标和值target为0(大于等于0),当i指针指向元素大于0时(由于数组排完序,后续所有元素都大于0),因此三个指针位置的值相加必定不满足条件,可以后续遍历(因为后续遍历i后移,第一个值依然大于0):即if (nums[i] > 0) return result;

除此之外还需要剪枝的遍历是 if (i > 0&& nums[i] == nums[i - 1]) continue;当i所指元素值与上一个元素值相同时,这一次遍历能满足的nums[i] + nums[left] + nums[right] == 0在上一次遍历必定存在,即两次重复,因此可以直接跳过此次遍历。需要注意的是不能写成if (i > 0&& nums[i] == nums[i + 1]) continue;即不能通过下次遍历去跳过本次遍历,因为这种写法会将三元组中相邻元素值相同的组合忽略,即我们就把 三元组中出现重复元素的情况直接pass掉了。 例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了,即不能直接忽略,因为此时还并未加入三元组。我们要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!left和right指针的去重可以通过if (i > 0&& nums[i] == nums[i + 1]) continue;实现,因为这两个的去重是在找到三元组后操作的,如果两个元素相同,则已经加入三元组,可以直接忽略。(困扰了一会才想明白)

然后在每一轮剪枝判断结束后,我们通过left和right来不断逼近target,此时的双指针移动与滑动窗口类似,当三数之和大于0时,将right左移缩小和的值:if (nums[i] + nums[left] + nums[right] > 0) right--;当三数之和小于0时,将left右移增大和的值:if(nums[i] + nums[left] + nums[right] < 0) left++;

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 > 0&& nums[i] == nums[i - 1]) continue;

            int left = i+1;
            int right = nums.size() - 1;
            while (left < right)
            {
                if (nums[i] + nums[left] + nums[right] > 0) right--;
                else if(nums[i] + nums[left] + nums[right] < 0) left++;
                else
                {
                    result.push_back({ nums[i],nums[left],nums[right] });
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    left++;
                    right--;
                }
            }
        }
        return result;
    }
};

四数之和

18. 四数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

你可以按 任意顺序 返回答案 。

与三数之和的实现方法类似,也是通过双指针实现,由于需要遍历i,j指针,以及双指针因此复杂度为O(n^3)。需要注意的不同地方是剪枝判断,由于目标值target小于0,因此直接判断nums[i]>target是不对的,当target小于0时,nums[i]即使大于target,与后面小于0的数相加可能反而小于target,因此判断条件为if (nums[k] > target&&nums[k]>=0) return result;同理第二层循环条件为if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) break;其他地方与三数之和几乎一样,不再阐述。

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] > target&&nums[k]>=0) return result;
            if (k > 0 && nums[k] == nums[k - 1]) continue;
            for (int i = k+1; i < nums.size(); i++)
            {
                if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 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]+nums[i] + nums[left] + nums[right] > target) right--;
                    else if ((long)nums[k]+nums[i] + nums[left] + nums[right] < target) left++;
                    else
                    {
                        result.push_back({ nums[k],nums[i],nums[left],nums[right] });
                        while (right > left && nums[left] == nums[left + 1]) left++;
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
};

  • 19
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值