代码随想录算法训练营第7天|【哈希表02】454.四数相加II ,383. 赎金信 ,15. 三数之和 , 18. 四数之和

今日任务: 454.四数相加II ,383. 赎金信 ,15. 三数之和 , 18. 四数之和 + 总结
状态:1刷

文章目录

  • 一、454.四数相加II
  • 二、383. 赎金信
  • 三、15. 三数之和
  • 四、18. 四数之和
  • 总结


一、454.四数相加II

题目: 给定四个包含整数的数组列表 A , B , C , D ,计算有多少个元组 (i, j, k, l) ,使得 A[i] + B[j] + C[k] + D[l] = 0。

思路: This is a C++ implementation for the problem where we need to count the number of i, j, k, l such that A[i] + B[j] + C[k] + D[l] equals to zero. The solution uses the concept of Hashmap and the meet in the middle algorithm.
The solution has four steps:

  1. Creat a hashmap to store the sum of all possible pairs in arrays A and B and their frequency.
  2. Loop through each element in A and for each element in A loop through each element in B, calculate their sum, and increase the frequency of sum in the hash map.
  3. Initialize a counter variable to store the number of combinations of element from A, B, C and D that sum to zero.
  4. Loop through each element in C and for each element in C loop through each element in D, calculate their sum, subtract it from zero, and check if the result is in the hashmap. if it is, it means there exists a pair in A and B that when added with the current pair from C and D results in zero. Add the frequency of that pair from hash map to the counter
  5. Return the counter

代码:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> umap;// key:a+b的数值,value:a+b数值出现的次数
        // 遍历前两个数组,将两数之和以及出现次数存到 umap
        // Traverse  arrays A and B, calculate the sum, and store the frequency in the hashmap
        for (int a : nums1){
            for (int b : nums2 ){
                umap[a + b]++;
            }
        }
        int count = 0;
        // 遍历后两个数组,若找到两数之和为 -(a+b),返回 value
        // Travese arrays C and D, check if the negative sum exists in the hashmap, and update the counter
        for (int c : nums3){
            for (int d : nums4){
                // 在 umap 中找到了 -(c+d) 后,存储 value
                if (umap.find(0 - (c + d)) != umap.end()){
                    count += umap[0 - (c + d)];
                }
            }
        }
        return count;
    }
};

时间复杂度: O(n^2)
空间复杂度: O(n^2)
The time complexity of this solution in O(n^2) because we loop through the arrays twice.
The space complexity is also O(n^2) because A and B could be different.


二、383. 赎金信

题目: Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise.

Each letter in magazine can only be used once in ransomNote.

思路: 数组在哈希法中的应用: 用一个长度为26的数组还记录magazine里字母出现的次数。然后再用ransomNote去验证这个数组是否包含了ransomNote所需要的所有字母。

The solution uses an integer array record of size 26(represent 26 alphabets) as a hashtable that stores the frequency of each character present in the magazine.
Here is how the solution works:

  1. It first checks if the size of the ransomNote is greater than the size of the magazine. if it is, then it immediately returns false because it would be impossible to construct the ransomNote from the magazine .
  2. Then it iterates over the magazine string and for each chartcter, it increments the corresponding index value in the record array. The index is calculated by subtracting ‘a’ from the character(to get values from 0 - 25).(key)
  3. Next,it iterates over the ransomNote string and for each character, it decrements the value in the record array. If it encounter a character that makes the count in the record array less than zero, it means this character appears more times in the ransomNote than it does in the magazine, it returns false.
  4. If it can iterate over the ransomNote without finding any character that appears more times in the ransomNote than in the magazine, it returns true.

The time complexity of this solution is O(n)
The space complexity is O(1).

Code:

// 数组在哈希法中的应用
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] ={0};
        if (ransomNote.size() > magazine.size()){
            return false;
        }
        for (int i = 0; i < magazine.size(); i++){
            record[magazine[i] - 'a']++;
        }
        for (int j = 0; j < ransomNote.size(); j++){
            record[ransomNote[j]- 'a']--;
            if (record[ransomNote[j]- 'a'] < 0){
                return false;
            }
        }
        return true;
    }
};

三、15. 三数之和

题目:
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
思路:
因为三元组不能重复,所以 use sorting and 双指针方法 two-pointer technique to find unique triplets in the array that sum to zero.

  1. First, usesort(nums.begin(), nums.end()), which is a important preprocessing step.
  2. Then, iterates over the sorted array, find the total sum is zero.
  3. To find these two numbers, uses two pointers, left and right, starting from the element next to i and from the end of the array, respectively. Then, these two pointers move towards each other until they meet.
  4. At each step, the solution checks the sum of nums[i] + nums[left] + nums[right]. If the sum is more than zero, the right pointer moves to the left. If the sum is less than zero, the left pointer moves to the right. If the sum is zero, it has found a triplet that satisfies the condition, which is then added to the result vector result.
  5. The solution also includes an important optimization of skipping over duplicate triplets. After a triplet that satisfies the condition is found, both the left and right pointers skip over all duplicate elements.
  6. Finally, the result vector result containing all triplets that satisfy the condition is returned.

Time and Space Complexity Analysis
The time complexity of this solution is O(n^2)
The space complexity is O(1)

代码:

// 双指针法
// 注意:不能重复,需要去重
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;// 2维数组
        // a = nums[i], b = nums[left], c = nums[right]
        //  sort
        sort(nums.begin(), nums.end());
        for (int i =  0; i < nums.size(); i++){
            if (nums[i] > 0){
                return result;
            }
            // 对i去重 使用 nums[i] == nums[i - 1]
            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 {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重,同理,跟前一位比
                    // 先收获 再去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    //
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
};

四、18. 四数之和

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

思路:

在上一题的基础上加一个 loop . 剪枝与去重是细节需要注意
1.排序数组: 第一步是对数组进行排序。这使得可以应用双指针技巧。此外,它让我们能够以一种简单且有效的方式跳过重复的四元组。

2.遍历数组: 解决方案然后包含两个嵌套循环遍历数组。外循环变量k和内循环变量i分别是四元组的第一和第二个元素。

3.剪枝: 如果当前对的和nums[k] + nums[i]大于目标值,并且两个数都是非负数,我们就跳出内循环。这是因为,在一个已排序的数组中,如果在这一点上总和已经超过目标值,那么每一对后续的数也将超过目标值。

4.跳过重复项: 如果当前元素与前一个元素相同,我们将跳过此次循环迭代。这样做是为了避免在结果中多次包含相同的四元组。

5.应用双指针技巧: 对于四元组的第三和第四个元素,我们使用两个指针,left和right,它们开始时分别位于元素i的旁边和数组的末尾。然后这两个指针向着彼此的方向移动,直到它们相遇。在每一步,我们检查四个元素的和。如果和大于目标值,我们将right指针向左移动。如果和小于目标值,我们将left指针向右移动。如果和等于目标值,我们将四元组添加到结果中。

6.避免溢出: 在检查总和时,代码将总和转换为long,以避免整数溢出。

7.再次跳过重复项: 在我们找到一个四元组,其总和等于目标值后,我们将left和right指针向内移动,跳过任何重复的元素。这样做是为了避免在结果中多次包含相同的四元组。

8.返回结果: 最后,函数返回包含所有与目标值相加的唯一四元组的result向量

Code:

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++){
            // 剪枝 nums[k] > target 要是负数呢
            if (nums[k] > target && nums[k] >= 0) {
            	break; // 这里使用break,统一通过最后的return返回
            }
            // 对nums[k]去重
            if (k > 0 && nums[k] == nums[k - 1]) {
                continue;
            }
            for (int i = k + 1; i < nums.size(); i++) {
                // 2级剪枝处理
                if (nums[k] + nums[i] > target && nums[k] + nums[i] >= 0) {
                    break;
                }
                // 对nums[i]去重
                if (i > k + 1 && nums[i] == nums[i - 1]) {
                    continue;
                }
                int left = i + 1;
                int right = nums.size() - 1;
                while (right > left) {
                    // nums[k] + nums[i] + nums[left] + nums[right] > target 会溢出
                    if ((long) nums[k] + nums[i] + nums[left] + nums[right] > target) {
                        right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } else if ((long) nums[k] + nums[i] + nums[left] + nums[right]  < target) {
                        left++;
                    } else {
                        result.push_back(vector<int>{nums[k], nums[i], nums[left], nums[right]});
                        // 对nums[left]和nums[right]去重
                        while (right > left && nums[right] == nums[right - 1]) right--;
                        while (right > left && nums[left] == nums[left + 1]) left++;

                        // 找到答案时,双指针同时收缩
                        right--;
                        left++;
                    }
                }
            }
        }
        return result;
    }
};

总结

没空写了,2刷补上

你可能感兴趣的:(代码随想录算法训练营,散列表,数据结构,哈希算法,leetcode,c++)