day7|LeetCode: 454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

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

c++代码(哈希map)

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map <int,int> result_map;
        for (int num1 : nums1) {
            for (int num2 : nums2 ) {
                int t = num1 + num2;
                result_map[t]++;
            }    
        }
        int res = 0;
        for (int num3 : nums3) {
            for (int num4 : nums4 ) {
                int t = 0 - (num3 + num4);
                res += result_map[t];
            }    
        }
        return res;

    }
};

思路

 要求四个数组中互相选一个元素等于0的有几个,

一开始我们会想到四层循环

再想到三层循环,得到前三个的和,再通过比较第四个数的互补数判断一下这个数是否与和相等,相等就说明有一个,还是超时了

就可以想到两成循环,利用空间换时间的方法,先把前面两个数的  和 先储存起来,再看第四个和第三个数的和的互补数再集合中后是否出现过,如果出现过,就找到了结果

  1. 因为需要查找集合中的数,就用哈希表储存值,因为数据范围够大,不能用数组储存
  2. 因为如果前两个数的和出现了多组,怎么办,set只能存一个值,且不能重复,所以用map,key存两个数的和,value存和出现的次数
  3. 当后两个数的和的互补数出现过时,加上value,就能知道有几组结果了


题目链接:383. 赎金信

c++代码(数组)

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

        return true;
    }
};

思路

思路和

242.有效的字母异位词 (opens new window)

几乎一样,但是第一个数组是第二个数组的子集,所以有些不一样,我们还可以用那种方法,先把一个数组中的元素按照字母顺序储存起来,然后遍历另外一个数组,判断元素出现过没,出现过就--,最后遍历结果集,如果出现了负数,就不是,返回false;

  • 第一点“为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思”  这里说明杂志里面的字母不可重复使用。

  • 第二点 “你可以假设两个字符串均只含有小写字母。” 说明只有小写字母,这一点很重要

几个问题:

  1. 我们需要先储存哪个数组,放到结果集有效的字母异位词因为组成相同,哪个都行最后一定为0,但是这次我们的数组个数不相同,我们应该,如果先储存小的,怎么都会出现负数,不满足题意,所以我们储存数组元素多的那个数组

其实在本题的情况下,使用map的空间消耗要比数组大一些的,因为map要维护红黑树或者哈希表,而且还要做哈希函数,是费时的!数据量大的话就能体现出来差别了。 所以数组更加简单直接有效!

  1. 怎么在集合中存元素,选哪个集合呢,因为都是由字母组成的,如果用数组当哈希可以开一个26大小的几个存,因为集合中的元素会重复,需要存出现次数,所以用数组或map,但是题目范围很小,用int存的下,这次用数组,数组下标相当map中的key,值相当于value

步骤

  • 先遍历第二个数组,出现了,那个对应下表值就++
  • 再遍历第一个,出现了--
  • 最后结果集出现了负数,说明第一个数组中有元素是第二个中没有的,不是子集
  • 输出


题目链接:15. 三数之和

 c++代码(双指针)

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[left] + nums[right] + nums[i] > 0) {
                    right = right - 1;
                } else if (nums[left] + nums[right] + nums[i] < 0) {
                    left = left + 1;
                } 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;
    }
};

思路

排序数组,先遍历固定第一个数,另外两个数遍历后面的的那一部分,分别用left和right储存,当三个数相加大于target(0),right退后,当小于0,left向前,找到结果,加入结果集,如果left<right就结束(相遇)。

为什么用双指针

四数相加是四个不同数组的元素相加,但是两数,三数之和在一个一个数组中,两数之和还能通过互补数,但三数之和就不行了,因为通过下表找不到,那能相加再互补吗,这样遍历也很难,都遍历一个数组,所以不能,用双指针就能解决,

 

怎么去重

因为结果中不能有顺序数值说明都相同的结合数组,所以要进行去重
a的去重

            if (i != 0 && nums[i] == nums[i-1]) {
                  continue;   
            }

为什么要和前一个比较,而不是nums[i] == nums[i+1]呢,因为真正结果可能具有重复元素

例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。

那这样为什么可以去重呢

因为这个元素固定后,把后面所有的可能都遍历完了。所以再第一个再遇到这个数(还是这个数),只会遇到重复的或只会遇到已经遍历过的就可以排除; 

 left和right的去重

当后面一个元素与left相同时和那个组合还是一样的,所以就可以让left++,同理遇到这种情况让right--,当得到一组数据时,还需要继续进行,只有left和right遇到才结束,这是为了第一个元素的筛选

怎么剪枝

            

            if (nums[i] > 0) return result;

当第一个元素都比0大时就可以

        sort(nums.begin(), nums.end());

                   result.push_back(vector<int>{nums[i],nums[left],nums[right]});
                    //插入vector后面不需要别名直接为数组
                   while (left < right && nums[left] == nums[left + 1]) left++;
                   //如果没遇到,且相同
                   while (left < right && nums[right] == nums[right - 1]) right--;                   
                   left++;
                   //下一个
                   right--;
        

 



题目链接:18. 四数之和

c++代码(双指针)

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[j] + nums[i] > target && nums[j] >= 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]});
                        while (left < right && nums[left] == nums[left + 1]) left++;
                        while (left < right && nums[right] == nums[right - 1]) right--;

                        left++;
                        right--;
                    }
                 }

             }
         }

         return result;
    }
};

思路

和上面三数之和一样,但是是双重循环,每个循环都要进行去重和剪枝

剪枝

因为这是和为target所以剪枝时加上下一个数可能变小,所以应该减少这个条件来完成剪枝,

从nums[i]>taeget变成了nums[i] > target && nums[i] >= 0, 因为nums[i]>=0,不管target是负数还是正数都包含了这种情况

第二个位置的去重变成了nums[i]+nums[j]>=target 

去重

和三数之和都是一样的

问题

四个数加起来可能会比int范围大

所有用long,long一个就行了,后面会自动转化 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值