LeeCode||454. 四数相加 II 383. 赎金信 ● 15. 三数之和 18. 四数之和

新的一周又是补进度的一周。。。

每道题的方法:

最后总结写

题目一链接:454. 四数相加 II - 力扣(LeetCode)

思路:先便利a,b数组,统计a + b出现的次数,再便利c,d数组,看umap映射里是否出现0 - (c + d) 的值,如果出现,就让count加上umap里值等于0 - (c + d)的个数。。。

代码:

class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map <int,int> umap;
        //先便利a,b数组,统计a + b出现的次数
        for(int a : nums1) {
            for(int b : nums2) {
                umap[a + b] ++;
            }
        }
        //在便利c,d数组,看umap映射里是否出现0 - (c + d) 的值,如果出现,就让count加上umap里值等于0 - (c + d)的个数
        int count = 0;
        for(int c : nums3) {
            for(int d : nums4) {
                if(umap.find(0 - (c + d)) != umap.end()) {
                    count += umap[0 - (c + d)];
                }
            }
        }

        return count;
    } 
};

难点:

解释细节1:因为a + b的值很大,如果用数组的话不好确定数组的大小。

解释细节2:map类似于数组,它的key是数组的下标而它的value则是数组的值

解释细节3:因为查询时用的是map而不是C,D两个数组,因此要查找的数是a + b的值

解释细节4:因为此题不需要去重,所以只需要统计二者由多少个符合条件的组合就行,所以需要加上每种可能的总数,即value值。




题目二链接:383. 赎金信 - 力扣(LeetCode)

思路:用到哈希表里的数组,将小写字母转化为26个数组下标然后进行计数

代码:

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

        for(int j = 0;j < magazine.size();j ++) {
            c[magazine[j] - 'a'] --;
        }

        for(int i = 0;i < 26;i ++) {
            if(c[i] > 0) return false;
        }
        return true;
    }
};

难点:




题目三链接:15. 三数之和 - 力扣(LeetCode)

思路:利用双指针算法将所有符合条件并去重的结果填入二维数组result里,最后return result:首先利用i指针遍历数组nums,再用left和right指针分别从两侧向中间移动直到left > right,在此过程中通过left和right和i指针的移动进行去重。

代码:

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        //开一个二维容器
        vector<vector<int>> result;
        //必须给nums先排序
        sort(nums.begin(),nums.end());

        for(int i = 0;i < nums.size();i ++) {
            //先移动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;
            //根据三数之和排除一些left和right的值
            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(right > left && nums[left] == nums[left + 1]) left ++;
                    while(right > left && nums[right] == nums[right - 1]) right --;

                    left ++;
                    right --;
                }
            }
        }

        return result;
    }
};

难点:

解释细节1:因为map统计出答案后不容易进行去重操作

操作细节3:因为i的右侧是left,如果让i = i + 1,i 有可能会等于left,而题目要求i与left,right互不相等

操作细节4:因为只有right向左移会使得三数之和变小,同理left

操作细节5:跟i去重其实是一个道理区别在于nums[left]和它右边的数比,,,

补充细节:当三个指针都经过去重后,那么最终答案就是去重的




题目四链接:18. 四数之和 - 力扣(LeetCode)

思路:首先创建一个二维数组容器result,然后对原来的数据进行排序,以便于后续利用双指针法;
然后开始双指针法:一共设置四个指针,这四个指针分三层,第一层是k,第二层是i,第三层是left和right;然后在第一层和第二层的指针中进行减枝和去重操作,第三层指针只进行去重操作,最后输出result.

代码:

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 ++) {
            //第一层减枝:target可能是负数,而负数可能越加越小,因此必须保证target大于零才能用这个方法减枝
            if(target >= 0 && nums[k] > target) break;
                //第一层去重:
                if(k > 0 && nums[k] == nums[k - 1]) continue;
                else {
                    for(int i = k + 1;i < nums.size();i ++) {
                        //第二层减枝:原理同上
                        if(target >= 0 && nums[k] + nums[i] > target) break;
                            //第二层去重:if判断条件比对第一层去重
                            if(i > k + 1 && nums[i] == nums[i - 1]) continue;
                            else {
                                int left = i + 1;
                                int right = nums.size() - 1;
                                //移动left和right指针:
                                //防止加和溢出在前面加long:
                                while(left < right) {
                                if((long)nums[k] + nums[i] + nums[left] + nums[right] > target) right --;      
                                //防止加和溢出在前面加long,long long 会慢一些
                                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]});
                                    //第三层去重:注意while里面必须在right > left 的前提下再进行操否则会导致数组越界访问,此处的界指的是第一层while()里的判断条件
                                    while(right > left && nums[left] == nums[left + 1]) left ++;
                                    while(right > left && nums[right] == nums[right - 1]) right --;

                                    left ++;
                                    right --;
                                }
                                }
                            }
                            
                    }
            }
        }

        return result;
    }
};

难点:

解释细节1:方便后续双指针算法

解释细节2:必须保证target大于零才能判断后续加和会越加越大,因为负数越加越小

解释细节3:首先i大于k,所以i的初始值必须大于k ,又因为i的右面有left所以nums[i]需要和nums[i - 1]比较,所以i必须大于k + 1;i的范围还可以参考第一层去重的条件

解释细节4:分别跟左右作比较

解释细节5:加和优先级大于(long)所以加前面就行,避免数超范围

解释细节6:如果去重里面的while不加上left<right,会导致数组越界访问

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值