day7 哈希表(二)

Day7 哈希表(二)
2023.12.6
深感抱歉,这几天事情太多了,昨天跟导师出差,整理资料到半夜,一直没空打卡学习,今晚难得有空,先将昨天12.5日的任务补上。

1. 454四数相加Ⅱ
这道题其实还是很好理解的,四个数组的匹配问题,麻瓜一点就是四层for,显然不是我们想要的最优解,尽量减少时间复杂度。延续之前做哈希表题的思想,固定一个值,然后找另一个值。但此处是四个值,因此需要做组合,两两组合或者三一分配。显然两两组合时间复杂度小一点,将四个值变为两个值匹配。分别两层for,将四值之和变为两值之和。
另一个技巧,本题找的是和为0的,我们转换成两个值后,也就是互为相反数,因此代码逻辑比之前的要简单一些。直接看另一个数是否在哈希表中即可。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map;
        for(int a:nums1){  //二合一
            for(int b:nums2){
                map[a+b]++;
            }
        }
        int count=0;
        for(int c:nums3){ //二合一且查找匹配
            for(int d:nums4){
                if(map.find(0-(c+d))!=map.end()){
                    count += map[0-(c+d)];
                }
            }
        }
        return count;
    }
};

2. 383赎金信
这道题很简单,做了之前的哈希表问题,再看这道题就很easy了,将对照字符串遍历,值++;然后遍历待处理字符串,值–;最后遍历map,如果有值<0的,那就问题啦!

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        unordered_map<int,int> map;
        for(char s:magazine){
            map[s-'a']++;
        }
        for(char s:ransomNote){
            map[s-'a']--;
        }
        for(auto i = map.begin();i!=map.end();i++){
            if(i->second<0){
                return false;
            }
        }
        return true;
    }
};

3. 15三数之和
这道题最开始我就想的继续用map,但代码逻辑很乱,一直写不出来,最后看代码随想录,emmm,绷不住了,这种方法果然不是最优解,本题应该使用双指针,即一个for控制遍历,然后定义一个指针指向i+1,另一个指向末尾。不过,前提是数组要先排序!很巧妙,然后三者值相加,判断大于零还是小于零,大于零,那正数就要变小,所以右侧的左移。小于零,负数要变大,因此左侧的要右移。整体逻辑简单。但存在很多细节。
1.如果遍历i的值,nums[i]都是大于0的,那还有必要继续吗?最小值大于零,三者之和肯定不能为0了。因此直接return。
2.题目要求不重复,就要对三个数进行删选,即相邻值相同的略过

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;
            int left = i +1;
            int right = nums.size()-1;
            if(i>0 && nums[i]==nums[i-1])
                continue;
            while(left<right){
                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(left<right && nums[right]==nums[right-1])
                        right--;
                    while(left<right && nums[left]==nums[left+1])
                        left++;
                    right--;
                    left++;
                }

            }
        }
        return result;
    }
};

4. 18四数之和
本题与三数之和相同,但外面多一层循环,并且,这次target是指定值,因此在剪枝操作就不能>0就return了,具体代码(用的代码随想录原始代码,因为在出差期间,时间不够,就偷个懒。。。)如下:

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++) {
            // 剪枝处理
            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;
    }
};
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值