LeetCode Day7

366 阅读7分钟

454.四数相加 II

给给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。 为了使问题简单化,所有的 A, B, C, D 具有相同的长度 N,且 0 ≤ N ≤ 500 。所有整数的范围在 -2^28 到 2^28 - 1 之间,最终结果不会超过 2^31 - 1 。 例如: 输入:

  • A = [ 1, 2]
  • B = [-2,-1]
  • C = [-1, 2]
  • D = [ 0, 2]

输出:

2 解释: 两个元组如下:

  1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0
  2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0

思路

  1. 初始化哈希表和计数器
  • 在函数内部,定义一个 unordered_map<int,int>,命名为 um。这个哈希表用于存储 nums1nums2 中元素的和及其出现次数。
  • 定义一个整数 count 并初始化为 0。这个变量用于存储满足条件的四元组的数量。
  1. 填充哈希表
  • 使用两层循环遍历 nums1nums2 的所有元素组合。
  • 对于每一对 (i, j),计算它们的和,并在哈希表 um 中增加这个和的计数。
  1. 计算满足条件的四元组数量
  • 使用另外两层循环遍历 nums3nums4 的所有元素组合。
  • 对于每一对 (k, l),计算它们的和的相反数。
  • 检查这个相反数是否在哈希表 um 中有非零的计数。如果是,将这个计数加到 count 上。
  1. 返回结果
  • 函数返回 count,表示满足条件的四元组的数量。

题解

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> um;
        int count=0;
        for(int i:nums1){
            for(int j:nums2){
                um[i+j]++;
            }
        }
        for(int k:nums3){
            for(int l:nums4){
                if(um[0-(k+l)]!=0){
                    count+=um[0-(k+l)];
                }
            }
        }
        return count;
    }
};

383.赎金信

给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串,判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构成。如果可以构成,返回 true ;否则返回 false。 (题目说明:为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。杂志字符串中的每个字符只能在赎金信字符串中使用一次。) 注意: 你可以假设两个字符串均只含有小写字母。 canConstruct("a", "b") -> false canConstruct("aa", "ab") -> false canConstruct("aa", "aab") -> true

思路

242.有效的字母异位词几乎是一模一样,唯一的区别就是第二个字符串并不一定和第一个字符串的构成完全一致,第二个字符串能把第一个字符串覆盖并且能有多余的字母,这就意味着我们在最后一步判别的时候数组的值可以是负数(242是必须为0)

题解

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 false;
        }
        return true;
    }
};

15.三数之和

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。 注意: 答案中不可以包含重复的三元组。 示例: 给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]

思路

在“三数之和”问题中,双指针技巧可以帮助我们在 (O(n)) 的时间内找到所有与给定数字组成和为0的对。 以下是使用双指针技巧解决“三数之和”问题的详细步骤:

  1. 排序
  • 首先,对数组进行排序。排序的目的是让我们能够轻松地使用双指针技巧,并跳过重复的数字。
  1. 固定一个数字
  • 遍历数组,对于每个数字 nums[i],我们将其视为固定的数字,并使用双指针找到所有与 nums[i] 组成和为0的对。
  1. 设置双指针
  • 对于固定的 nums[i],设置两个指针,leftrightleft 指针初始位置为 i+1right 指针初始位置为数组的最后一个元素。
  1. 移动双指针
  • left 指针在 right 指针的左侧时,计算三个数字的和:sum = nums[i] + nums[left] + nums[right]
    • 如果 sum 等于0,那么我们找到了一个有效的三元组。将这三个数字添加到结果集中。
    • 为了避免重复的三元组,我们需要跳过所有重复的数字。因此,如果 nums[left]nums[left+1] 相同,我们会增加 left 指针,直到找到一个不同的数字。同理,如果 nums[right]nums[right-1] 相同,我们会减少 right 指针。
    • 之后,left 指针向右移动一位,right 指针向左移动一位,继续寻找其他可能的三元组。
    • 如果 sum 小于0,这意味着我们需要一个更大的数字来使三数之和为0。因此,增加 left 指针。
    • 如果 sum 大于0,这意味着我们需要一个更小的数字来使三数之和为0。因此,减少 right 指针。
  1. 跳过重复的固定数字
  • 为了避免重复的三元组,当我们移动到下一个固定数字 nums[i+1] 时,如果它与 nums[i] 相同,我们会跳过它。

题解

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

18.四数之和

题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。 注意: 答案中不可以包含重复的四元组。 示例: 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]

思路

这是一个“四数之和”问题,它是“三数之和”问题的扩展。为了解决这个问题,我们可以使用两层循环固定前两个数字,然后使用双指针技巧找到与这两个数字组成和为目标值的对。 以下是解决此问题的步骤:

  1. 排序数组
  • 首先,对数组进行排序。排序的目的是让我们能够轻松地使用双指针技巧,并跳过重复的数字。
  1. 初始化结果集
  • 创建一个空的结果集 res,用于存储所有满足条件的四元组。
  1. 固定前两个数字
  • 使用两层循环遍历数组,对于每对数字 nums[i]nums[j],我们将它们视为固定的数字,并使用双指针找到所有与 nums[i]nums[j] 组成和为目标值的对。
  1. 设置双指针
  • 对于固定的 nums[i]nums[j],设置两个指针,leftrightleft 指针初始位置为 j+1right 指针初始位置为数组的最后一个元素。
  1. 移动双指针
  • left 指针在 right 指针的左侧时,计算四个数字的和。
    • 如果和等于目标值,那么我们找到了一个有效的四元组。将这四个数字添加到结果集中。
    • 为了避免重复的四元组,我们需要跳过所有重复的数字。
    • 如果和小于目标值,增加 left 指针。
    • 如果和大于目标值,减少 right 指针。
  1. 跳过重复的固定数字
  • 为了避免重复的四元组,当我们移动到下一个固定数字时,如果它与前一个数字相同,我们会跳过它。

题解

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