代码随想录刷题记录(6)| 哈希表(454.四数相加II,383. 赎金信,15. 三数之和,18. 四数之和,总结)

目录

(六)四数相加Ⅱ

1.  题目描述

2. 思路

3. 解题过程

(七)赎金信

(八)三数之和

1. 题目描述

2. 思路

3. 解题过程

(1)超时的代码 

(2)代码随想录的正确哈希

 (3)双指针

(九)四数之和

1. 题目描述

2. 思路

3. 解题过程

(十)总结


(六)四数相加Ⅱ

454. 四数相加 II - 力扣(LeetCode)

1.  题目描述

        给你四个整数数组 nums1nums2nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

2. 思路

  1. 首先定义 一个 unordered_map,key 放 a 和 b 两数之和,value 放 a 和 b 两数之和出现的次数。
  2. 遍历大 A 和大 B 数组,统计两个数组元素之和,和出现的次数,放到 map 中。
  3. 定义 int 变量 count,用来统计 a+b+c+d = 0 出现的次数。
  4. 在遍历大 C 和大 D 数组,找到如果 0-(c+d) 在 map 中出现过的话,就用 count 把 map 中 key 对应的 value 也就是出现次数统计出来。
  5. 最后返回统计值 count。

3. 解题过程

难易程度:中等

标签:数组、哈希表

        result 计算的时候是加上有几个等于此值的和,而不是++,第一次犯了这个错误。 

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map;    // nums1和nums2的两元素和与出现次数
        for(int i = 0; i < nums1.size(); i++){
            for(int j = 0; j < nums2.size(); j++){
                auto it = map.find(nums1[i] + nums2[j]);
                if(it != map.end()){
                    it->second++;
                }
                map.insert(pair<int, int>(nums1[i] + nums2[j], 1));
            }
        }
        int result = 0;
        for(int i = 0; i < nums3.size(); i++){
            for(int j = 0; j < nums4.size(); j++){
                auto it = map.find(-nums3[i] - nums4[j]);
                if(it != map.end()){
                    result += it->second;
                }
            }
        }
        return result;
    }
};

注意:其实不需要迭代器插入和让 value 自增,直接 map[ nums1[i] + nums2[j] ]++就可以了。

 

(七)赎金信

        上一篇的相关题目有这个,做过了。

(八)三数之和

15. 三数之和 - 力扣(LeetCode)

1. 题目描述

        给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != ji != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

        你返回所有和为 0 且不重复的三元组。

        注意:答案中不可以包含重复的三元组。

 

2. 思路

        可以使用哈希法来确定 0-(a+b) 是否在 数组里出现过,但题目中说的不可以包含重复的三元组,把符合条件的三元组放进vector中,然后再去重,这样是非常费时的,很容易超时,所以使用双指针。

3. 解题过程

难易程度:中等

标签:数组、双指针、排序

(1)超时的代码 
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        unordered_map<int, int> num_map;    // 把数字和下标存在map
        vector<vector<int>> result;
        for(int i = 0; i < nums.size(); i++){
            num_map[nums[i]] = i;
        }
        for(int i = 0; i < nums.size(); i++){
            for(int j = i + 1; j < nums.size(); j++){
                auto it = num_map.find(-nums[i] - nums[j]);
                if(it != num_map.end()){
                    if(it->second != i && it->second != j){
                        vector<int> temp(3);
                        temp = {nums[i], nums[j], it->first};
                        sort(temp.begin(), temp.end());
                        result.push_back(temp);
                        continue;
                    }
                }
            }
        }
        sort(result.begin(), result.end());
        result.erase(unique(result.begin(), result.end()), result.end());
        return result;
    }
};

(2)代码随想录的正确哈希

        这些去重实在有点过于复杂。。。还是放弃这种方法( 

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[j], c = -(a + b)
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            if (nums[i] > 0) {
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
                continue;
            }
            unordered_set<int> set;
            for (int j = i + 1; j < nums.size(); j++) {
                if (j > i + 2
                        && nums[j] == nums[j-1]
                        && nums[j-1] == nums[j-2]) { // 三元组元素b去重
                    continue;
                }
                int c = 0 - (nums[i] + nums[j]);
                if (set.find(c) != set.end()) {
                    result.push_back({nums[i], nums[j], c});
                    set.erase(c);// 三元组元素c去重
                } else {
                    set.insert(nums[j]);
                }
            }
        }
        return result;
    }
};

 (3)双指针

        首先将数组排序,然后有一层 for 循环,i 从下标 0 的地方开始,同时定一个下标 left  定义在 i+1 的位置上,right 在数组结尾的位置上。

        如果 nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,right 向左移动;如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,直到 left 与 right 相遇为止。

         注意:去重要在过程中完成,排序之后去掉连续的重复的。

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;
            }
            for(int left = i + 1, right = nums.size() - 1; left < right;){
                int temp = nums[i] + nums[left] + nums[right];
                if(temp == 0){
                    result.push_back({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--;
                }
                else if(temp < 0){
                    left++;
                }
                else{
                    right--;
                }
            }
        }
        return result;
    }
};

 

(九)四数之和

18. 四数之和 - 力扣(LeetCode)

1. 题目描述

        给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • abc 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target

        你可以按 任意顺序 返回答案 。

2. 思路

        四数之和,和三数之和是一个思路,都是使用双指针法, 基本解法就是在三数之和的基础上再套一层for循环。

        但是有一些细节需要注意,例如: 不要判断 nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回,因为 0 已经是确定的数了,四数之和这道题目 target 是任意值。比如:数组是 [-4, -3, -2, -1]target 是 -10,不能因为 -4 > -10 而跳过。但是我们依旧可以去做剪枝,逻辑变成 nums[i] > target && (nums[i] >=0 || target >= 0)就可以了。

 

3. 解题过程

难易程度:中等

标签:数组、双指针、排序

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> result;
        for(int i = 0; i < nums.size(); i++){
            // 这里可以直接return,因为如果第一个数就大于那么一定不可能加后面的得到结果
            if(nums[i] > target && nums[i] >= 0){
                return result;
            }
            if(i > 0 && nums[i] == nums[i - 1]){
                continue;
            }
            for(int j = i + 1; j < nums.size(); j++){
                // 要用break而不可以直接return,因为i+1还有可能有符合条件的
                if(nums[i] + nums[j] > target && nums[i] + 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){
                    // 注意范围,加的结果可能超出int界限
                    long long temp = (long long)nums[i] + nums[j] + nums[left] + nums[right];
                    if(temp == target){
                        result.push_back({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--;
                    }
                    else if(temp < target){
                        left++;
                    }
                    else{
                        right--;
                    }
                }
            }
        }
        return result;
    }
};

 

(十)总结

        一般来说哈希表都是用来快速判断一个元素是否出现集合里

        对于哈希表,要知道哈希函数哈希碰撞在哈希表中的作用。

        哈希函数是把传入的key映射到符号表的索引上。

        哈希碰撞处理有多个key映射到相同索引上时的情景,处理碰撞的普遍方式是拉链法和线性探测法。

        在C++中,set 和 map 都分别提供了三种数据结构,每种数据结构的底层实现和用途都有所不同,要根据他们的特点,判断使用哪种结构。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值