代码随想录算法训练营第七天 | 哈希表理论基础,454.四数相加II 383. 赎金信 15. 三数之和 18. 四数之和

454.四数相加II

常规思路为四个for循环嵌套,复杂度过高O(n^4);我分别用了两个哈希表,统计了两个数之和的出现次数;,结果再相乘。举例a数组元素+b数组元素 = 3,出现了2次;c数组元素+d数组元素 = -3,出现了4次;那么总的结果就是 2 * 4 = 8。PS:写入哈希表可以umap[n],读取哈希表为umap.at(n) 而不是 umap[n]。

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int,int> umap1;
        unordered_map<int,int> umap2;
        int sum = 0;
        for(auto s:nums1) {
            for(auto t:nums2) {
                umap1[s+t]++;
            }
        }
        for(auto s:nums3) {
            for(auto t:nums4) {
                umap2[s+t]++;
            }
        }
        for(auto n:umap1) {
            int tmp = n.first;
            if(umap2.find(-tmp) != umap2.end()) {
                sum += umap1.at(tmp) * umap2.at(-tmp);
            }
        }
        return sum;
    }
};

383. 赎金信  

采用哈希表记录出现的次数,因为magazine 中的每个字符只能在 ransomNote 中使用一次,所以通过计数加减进行判断。注意判断条件:不是哈希表的值s.second != 0而是 > 0,特例为第一个数组"abcd", 第二个数组"abcdabcd",这样判断完哈希表的值为负数,一开始疏忽了这点。

class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int count = 0;
        unordered_map<char,int> umap;
        for(auto s:ransomNote) {
            umap[s]++;
        }        
        for(auto s:magazine) {
            if(umap.find(s) != umap.end()){
                umap[s]--;
            } 
        }
        for(auto s:umap) {
            if(s.second > 0)//注意这里
                return false;
        }
        return true;
    }

};

15. 三数之和

这题做不来直接翻答案了。。。思路很简单,先排序接着使用三个指针计算和。但是要处理的条件很多,一不留神就会忽略。我看了思路然后做题,也没能考虑齐全条件,错误见注释。(这类题得review)        

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        std::sort(nums.begin(), nums.end());
        for(int i = 0; i < nums.size(); ++i) {
            if(nums[i] > 0) return res;

            //跳过重复的数字,不能nums[i] == nums[i+1],会跳过前两个数字相同的情况。
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            int j = i + 1;
            int k = nums.size() - 1;
            while(j < k) {
                if(nums[i] + nums[j] > 0) return res;
                if(nums[i] + nums[j] + nums[k] > 0) 
                    k--;
                else if(nums[i] + nums[j] + nums[k] < 0)
                    j++;
                else {
                    res.push_back({nums[i],nums[j],nums[k]});
                    
                    //因为使用了两个指针表示后面两个数字,所以前瞻性地使用nums[k]=nums[k-1]
                    //换句话说即使用k忽略了两个相同的数字,nums[j]也能表示这个被忽略的数字
                    //当然这么写也完全没问题,k < nums.size() - 1 && nums[k] == nums[k + 1]
                    while(j < k && nums[k] == nums[k - 1]) 
                        k--;  
                    while(j < k && nums[j] == nums[j + 1])
                        j++;
                    j++;
                    k--;
                }
            }
        }
        return res;
    }
};

18. 四数之和

除了双指针法想不到别的方法了,看了答案也只有这个办法。总结一下:

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        sort(nums.begin(),nums.end());
        for(int h = 0; h < nums.size(); ++h) {
            long long x = nums[h];
            //这里有负数的可能不能 x > target,比如说{-3,-2,-1},target = -5;
            // if(x > target && x >= 0) return res;
        
            if(h > 0 && nums[h] == nums[h - 1]) 
                continue;
            for(int i = h + 1; i < nums.size(); ++i) {
                long long y = nums[i];
                // if(x + y > target && x + y >= 0) return res;
                if(i > h + 1 && nums[i] == nums[i - 1]) 
                    continue;
                int j = i + 1;
                int k = nums.size() - 1;
                while(j < k) {
                    if((long)nums[h] + nums[i] + nums[j] + nums[k] > target)
                        k--;
                    else if((long)nums[h] + nums[i] + nums[j] + nums[k] < target)
                        j++;
                    else
                    {
                        res.push_back({nums[h],nums[i],nums[j],nums[k]});
                        while(j < k && nums[k] == nums[k - 1])
                            k--;
                        while(j < k && nums[j] == nums[j + 1])
                            j++;
                        k--;
                        j++;
                    }
                }
            }
        }        
        return res;
    }
};

1. 这类题去重要慎重,不能漏掉重复的数字。

2. 这儿要返回具体的数,但是哈希表只能反映次数和总和,所以不能用。

3. 双指针法能处理的也只有两个数字,数字变多后还是得放入循环中解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值