代码随想录算法训练营第六天 | 题目 LeetCode 第454题.四数相加II 题目LeetCode349. 两个数组的交集 题目 LeetCode LeetCode18. 四数之和

文章介绍了使用哈希表和双指针法解决LeetCode上的几道编程题目,包括四数相加II、赎金信和三数之和。在四数相加II中,通过计算两组数的和并存储在哈希表中,然后查找是否存在和为0的组合。赎金信问题利用哈希表记录字母出现次数判断能否构造赎金信。三数之和问题通过排序和双指针技巧找到所有和为目标值的三元组,同时处理了去重问题。
摘要由CSDN通过智能技术生成

 LeetCode 第454题.四数相加II 

 分析:先计算num1与nums2的和,并将其存储到哈希结构为unordered_map的容器m中,key作为两数之和,value是和出现的次数。

    再计算nums3和nums4的和,在m中找(0-(nums1+nums2)),如果能找到,说明存在和为0的四个数,最后统计满足和为0的元组的个数。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int>m;//key:a+b的数值,value:a+b数值出现的次数
        for(int i = 0 ;i < nums1.size();i++){
            for(int j = 0;j < nums2.size();j++){
                //统计nums1和nums2和的值及出现次数
                m[nums1[i]+nums2[j]]++;
            }
        }
        int count = 0; //统计满足和为零的元组的个数
        for(int c : nums3){
            for(int d : nums4){
                //如果0-(nums3+nums4)的差能够在m中找到
                if(m.find(0-(c + d)) != m.end()){
                    count += m[0-(c+d)];
                }
            }
        }
        return count;

    }
    
};

LeetCode 383. 赎金信 

分析:在本题中用数组作为哈希表,索引代表字母,索引对应的数组元素记录该字母出现的次数。若ransomNote中的字母有在magazine中出现(字母相同,magazine[i]-'a'和ransomNote - 'a'相等相同并作为索引,则该数组对应的索引记录了字母出现的次数),该索引(即字母)对应的数组元素--。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        //创建一个数组接收字母
        int record[26] = {0};
        //先判断ransomNote数组大小是否比magazine数组大
        if(ransomNote.size() > magazine.size()){
            return false;
        }
        //对每一个字母,记录其出现次数
        for(int i = 0;i < magazine.size();i++){
            record[magazine[i] - 'a']++;
        }
        //若ransomNote中的字母有在magazine中出现(字母相同,magazine[i]-'a'和ransomNote - 'a'相等相同并作为索引,则该数组对应的索引记录了字母出现的次数)
        for(int i = 0;i < ransomNote.size();i++){
            record[ransomNote[i] - 'a']--;
            if(record[ransomNote[i] - 'a'] < 0){
                return false;
            }
        }
        return true;
    }
};

LeetCode 15. 三数之和 

双指针法分析:(1)由于不需要返回数组元素下标,因此先对数组进行排序

(2)i是第1个元素的下标,首先判断排序后的第1个元素是否大于0,如果是,那么一定不会存在和为0的三个数,直接返回结果;

    对第一个数去重:首先,确定第1个数以后,通过循环进行第2、3个数的选择,找出与第1个数的和为零的所有组合;结束后,对下标+1,找到下一个第1个数,如果这个数和前一个数相同,那么已经存在了所有和为0的组合,因此跳出当前i循环,进行i+1循环。

一种错误的去重a方法:

// 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }

例如一个三元组{-1,-1,2},如果进行上述continu,那么用于确定left和right的i就是第二个-1的下标,此时将忽略组合{-1,-1,2},同时left和right是同一个值的下标,不满足三元组来自三个数的要求。

对于第1个数,要保证选取的是重复数中出现的第一个数,例如{-2,,-1,-1,-1,0,1,2,3,3,3}选取下标为1的-1。

(3)选择第2、3个数:在第1个数的下标确定的情况下,判断nums[I] + nums[left] + nums[right]的和;大于0,那么right左移(和变小);小于0,那么left右移(和变大);等于0,将三个数构成的三元组存入result;

(4)对第2、3个数去重:当出现nums[I] + nums[left] + nums[right] = 0时,进行left++、right--,找还有没有和当前第1个数nums[i]的和为0的数,如果在left < right 的情况下,nums[left+1] = nums[left],那么nums[right]是固定的,不可能会出现新的和为0的组合,因此要left++;因为数组是排好序的,相同的数都是连续存储的,因此采用while循环,跳过所有和当前第2个数相等的数。

第3个数去重也是同理。

去重逻辑要放在给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++){
            //如果排序后第一个数字大于0,那么直接返回结果
            if(nums[i] > 0){
                return result;
            }
            //去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            //定义双指针left 和right
            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{
                    //将和为0的三元组存入容器
                    result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    //在找到一个三元组时,对left和right去重
                    while(right > left && nums[left] == nums[left + 1]){
                        left++;
                    }
                    while(right > left && nums[right] == nums[right - 1]){
                        right--;
                    }
                    left++;
                    right--;
                }
            }
        }
        return result;

    }
};

LeetCode18. 四数之和

分析:采用双指针法计算三数之和、四数之和乃至五数之和、六数之和,最重要是确定left和right,是寻找最后两个数的索引。之前的数都可以用for循环进行剪枝、 去重后寻找。

与三数之和不同点在于:(1)目标target是任意数,不是0,在进行第1个数剪枝操作时,如果满足nums[i] > target && target >0 那么后面就不会有满足四数之和等于target的数了;同理,在进行第2个数剪枝操作时,满足nums[i] + nums[j] > target && target > 0,找不到符合要求的数。这是因为要考虑到负数的情况。

(2)要考虑整型溢出越界问题:nums[i] + nums[j] + nums[left] + nums[right]溢出,要使用long

变为:(long)nums[i] + nums[j] + nums[left] + nums[right].

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 && target > 0 && nums[i] > 0){
                break;//没有符合和为target的四个数,跳出循环
            }  
            //去重
            if(i > 0 && nums[i] == nums[i-1]){
                continue;
            }
            //对第二个数进行操作
            for(int j = i+1; j < nums.size(); j++){
                //剪枝
                if(nums[i] + nums[j] > target && target > 0){
                    break;
                }
                //去重
                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) 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]});
                        //对当前索引为left的元素去重
                        while(left < right && nums[left] == nums[left + 1]){
                            left++;
                        }
                        while(left < right && nums[right] ==nums[right - 1]){
                            right--;
                        }
                        right--;
                        left++;
                    } 
                }

            }
            
        }

    return result;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值