【代码随想录算法训练Day7】LeetCode 454.四数相加II 、LeetCode 383. 赎金信 、LeetCode 15. 三数之和、LeetCode 18. 四数之和

Day7 哈希表

今天是哈希表的第二天,又会有什么样的题在前面等着呢

LeetCode 454.四数相加II【哈希表】

你先来两数相加,现在四数相加了,而且四重暴力循环必定超时,怎么办呢?那就把四数分成两个两数不就行了,两个n2相加,复杂度还是O(n2)。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> umap;
        for(int a:nums1)
            for(int b:nums2)
                umap[a+b]++;
        int cnt=0;
        for(int c:nums3)
            for(int d:nums4){
                if(umap.find(0-(c+d))!=umap.end())
                    cnt+=umap[0-(c+d)];
            }
        return cnt;
    }
};

LeetCode 383.赎金信【哈希表】

题目背景:以前绑架犯为了不暴露赎金信字迹,要从杂志上搜索各个需要的字母,组成单词来表达意思。最后贴成的信件,大概就像P5里的预告信一样?
回到题目本身,这道题几乎和字母异位词完全一致,区别在于两个字符串长度无法作为判断标准,而且只需要单向满足条件即可,所以我们就先将待拼凑的magazine数组的字符情况存入,再用另一个减即可。

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

LeetCode 15.三数之和【双指针】

一刻也来不及为做出二数与四数之和而高兴,立马赶到战场的是——三数之和!
令人兴奋不已的三个数的和,我们的想法自然是把三数拆成2+1,先枚举第一个数,然后遍历后两个数,让后两个数之和满足target-第一个数。
多点同时遍历,首先想到的自然是双指针算法,本题用双指针的效率也很高,注释直接写到代码里了,注意结束的条件是后两个遍历指针从两边在中间重合了。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n=nums.size();
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        //枚举a
        for(int a=0;a<n;a++){
            //与a相同的数直接跳过
            if(a>0 && nums[a]==nums[a-1]) continue;
            //c对应的指针初始从数组最右端开始
            int c=n-1;
            int target=-nums[a];//转化为两数之和,即后两数之和=第一个数的相反数
            //枚举b
            for(int b=a+1;b<n;b++){
                //与b相同的数直接跳过
                if(b>a+1 && nums[b]==nums[b-1]) continue;
                //需要保证b的指针在c指针的左侧
                while(b<c && nums[b]+nums[c]>target) c--;
                //指针重合后,b再增加,就不会有满足b<c且和满足条件的c了,退出循环
                if(b==c) break;
                if(nums[b]+nums[c]==target)
                    res.push_back({nums[a],nums[b],nums[c]}); 
            }
        }
        return res;
    }
};

LeetCode 18.四数之和【哈希表】

在三数之和的基础上多加了一层循环遍历,同时要注意剪枝的条件。

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||target>=0)){
            	break; // 这里使用break,统一通过最后的return返回
            }
            // 去重
            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 || target>=0)) {
		    break;
		}
                // 正确去重方法
                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 (nums[k]+nums[i]>target-(nums[left]+nums[right])){
                        right--;
                        // 当前元素不合适了,可以去重
                        while (left<right && nums[right]==nums[right+1]) right--;
                    // nums[k] + nums[i] + nums[left] + nums[right] < target 会溢出
                    } else if (nums[k]+nums[i]<target-(nums[left]+nums[right])){
                        left++;
                        // 不合适,去重
                        while (left<right && nums[left]==nums[left-1]) left++;
                    } else {
                        result.push_back(vector<int>{nums[k],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;
    }
};

今天的题目做着好累,加完这个加那个,好好休息一下。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值