您现在的位置是:首页 >技术交流 >代码随想录算法训练营day6| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和网站首页技术交流

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

Hulmos626 2024-09-05 12:01:03
简介代码随想录算法训练营day6| 454.四数相加II 383.赎金信 15.三数之和 18.四数之和

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

LeetCode 454 四数相加II

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

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {  //解题思路是将四个数组分为两个相邻数组的和,然后把其中一个数组的值进行映射,value存放key出现次数
        unordered_map<int, int> umap;  //定义map数组
        int count = 0;  //初始化符合条件结果
        for(int i : nums1) {
            for(int j : nums2) {
                umap[i + j]++;  //记录i+j的次数
            }
        }
        for(int i : nums3) {
            for(int j : nums4) {
                if(umap.find(0 - i - j) != umap.end())
                    count += umap[0- i - j];
            }
        }
        return count;
    }
};

本题小结:将两两数组的和分为两个数组,然后把其中一个数组的值进行映射,最后进行判断计数累加即可。

LeetCode 383 赎金信

题目链接: 383. 赎金信

思路:本题判断第一个字符串ransom能不能由第二个字符串magazines里面的字符构成,但是这里需要注意两点:

  • 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思” 这里说明杂志里面的字母不可重复使用!
  • 第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母!!!
// 时间复杂度: O(n)
// 空间复杂度:O(1)
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};  //题目中假设仅含小写字母,定义计数数组
        for(char s : magazine) {
            record[s - 'a']++;  //对出现过的字符计数
        }
        for(char s : ransomNote) {
            record[s - 'a']--;  //对相同字符减一
            if(record[s - 'a'] < 0)  //如果计数小于0,无法构成
                return false;
        }
        return true;
    }
};

本题小结:假设仅含小写字母,定义计数数组record,查找时进行减一操作,最后判断计数数组的值。

LeetCode 15 三数之和

题目链接: 15.三数之和

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[j], c = -(a + b)
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
                continue;
            }
            unordered_set<int> set;
            for (int j = i + 1; j < nums.size(); j++) {
                if (j > i + 2
                        && nums[j] == nums[j-1]
                        && nums[j-1] == nums[j-2]) { // 三元组元素b去重
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if (set.find(c) != set.end()) {
                    result.push_back({nums[i], nums[j], c});
                    set.erase(c);// 三元组元素c去重
                } else {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

此种方法效率不高,应该使用双指针法

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, right = nums.size() - 1;
            while(left < right) {
                if(nums[i] + nums[left] + nums[right] > 0)  //三数之和大于0
                    right--;
                else if(nums[i] + nums[left] + nums[right] < 0)  //三数之和小于0
                    left++;
                else {
                    result.push_back(vector<int> {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--;  //左移
                }
            }
        }
        return result;
    }
};

本题小结:思路是先对数组进行排序,然后在一个循环中确定第一位数,通过双指针进行结果判断,但是需要进行剪枝优化。

LeetCode 18 四数之和

题目链接: 18. 四数之和

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;  //定义结果矩阵
        sort(nums.begin(), nums.end());  //对数组进行排序
        for(int i = 0; i < nums.size(); i++) {
            //进行剪枝优化
            if(nums[i] > target && nums[i] >= 0)
                break;
            //对第一个数进行去重
            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[i] + nums[j] >= 0)
                    break;
                //对第二个数进行去重
                if(j > i + 1 && nums[j] == nums[j - 1])
                    continue;
                //双指针
                int left = j + 1, right = nums.size() - 1;
                while(left < right) {
                    if((long)nums[i] + nums[j] + nums[left] + nums[right] > target)  //如果和大于目标,此处数据可能会溢出
                        right--;
                    else if((long)nums[i] + nums[j] + nums[left] + nums[right] < target)  //如果和小于目标,此处数据可能会溢出
                        left++;
                    else {  //保存到数组
                        result.push_back(vector<int> {nums[i], nums[j], nums[left], nums[right]});
                        while(left < right && nums[left] == nums[left + 1])  //对第三个数去重
                            left++;
                        while(left < right && nums[right] == nums[right - 1])  //对第四个数去重
                            right--;
                        //指针移动
                        left++;
                        right--;
                    }
                }
            }
        }
        return result;
    }
};

本题小结:与上题思想类似,此题多加一个循环,两重循环确定两位数,然后再采用双指针进行判断,此题的剪枝优化需要多加思考!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。