代码随想录训练营day7|454.四数相加2、383.赎金信、15.三数之和、18.四数之和

LeetCode454:四数之和2

方法依据:因为本质还是寻找元素中是否存在某一个数–> 哈希。
**思路:**这一题和两数之和很相似,就是变成了四个数组。
首先,将nums1nums2的和遍历出来,之后放到map中去,key为和,value为这个和出现的次数(因为最后返回的是次数。根据需要返回的结果做调整。)
随后,遍历nums3nums4的和,如果这个和的相反数在map中出现了,就可以将对应的次数取出来,在通过ans累加得到最后的结果。
**关键点:**将最后结果的组成形式 拆成两部分,先求一半,在求一办。不要被四个数组给吓到,大不了多嵌几层循环。

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        // 为什么选择使用哈希表:因为本质还是找到元素中是否存在某一个数。
        // 和两数之和很类似。只不过这里变成了四个数组
        // 只需要将nums1 和nums2看成一个,nums3和nums4看成另外一个即可。
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        int size = nums1.length;
        int ans = 0;
        int sum = 0;
        // 遍历nums1和nums2,的到所有的和,放入map中和为key,和出现的次数为value(因为最后返回的是次数,如果返回别的东西那就根据情况,例如返回下标)
        for(int i=0;i<size;i++){
            for(int j=0;j<size;j++){
                sum = nums1[i] + nums2[j];
                if(map.containsKey(sum)){
                    map.put(sum, map.get(sum) + 1);
                }
                else{
                    map.put(sum, 1);
                }
            }
        }

        // 遍历nums3和nums4 看看他们俩的和的相反数 是否出现在map中,
        // 出现了就可以根据value累加得到次数
        for(int i=0;i<size;i++){
            for(int j=0;j<size;j++){
                sum = nums3[i] + nums4[j];
                if(map.containsKey(-sum)){
                    ans += map.get(-sum);
                }
            }
        }
        return ans;
    }
}
//cpp
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> umap; // key a+b的值,value:a+b数值出现的次数
        for(int a: nums1){
            for(int b:nums2){
                umap[a+b]++;
            }
        }
        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;
    }
};

LeetCode383:赎金信

方法依据:因为本质是找元素、组合是否出现。
思路:先遍历magazine,得到每个字符及其个数放到map中,key为字符(这里用的是Integer,所以也可以用数组来构建哈希,省时间和空间。),value为对应字符的次数。接着遍历ransomNote,对出现在map中的元素减1,如果出现map中没有出现的元素,返回false;如果mapvalue出现负数则返回false
关键点:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 先遍历magazine,中每个字符,并统计个数,存到map。key为字符;value为当前字符的个数。
        // 遍历ransomNote,如果这个字符在map中存在就对应减1.最后判断map中的value是否有小于0的,如果有就返回false,否则返回true。
        // 看到了都是小写英文字母,所以也可以用数组来构建哈希。
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        for(char ch : magazine.toCharArray()){
            int delta = ch-'a';
            if(map.containsKey(delta)){
                map.put(delta, map.get(delta)+1);
            }
            else{
                map.put(delta,1);
            }
        }
        for(char ch: ransomNote.toCharArray()){
            int delta = ch-'a';
            if(map.containsKey(delta)){
                map.put(delta, map.get(delta)-1);
            }
            else{
                return false;
            }
        }
        for(HashMap.Entry<Integer, Integer> entry:map.entrySet()){
            if(entry.getValue() <0){
                return false;
            }
        }
        return true;
    }
}
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        if(ransomNote.size() > magazine.size())
        {
            return false;
        }
        unordered_map<char, int> umap;
        for(char strTemp:magazine){
            umap[strTemp]++;
        }
        for(char strTemp : ransomNote){
            if(umap[strTemp] == 0){ // 这里是先判断然后再减,因此和0比较
                return false;
            }
            umap[strTemp]--;
        }
        return true;
    }
};

LeetCode15:三数之和(需要二刷,去重环节)

**思路:**双指针不难想到,去重环节的细节很多。理清思路很重要。
首先,为了去重,我要将重复的元素放在一起,所以需要先给数组排序Array.sort(nums),得到一个从下到大的nums。
按照代码随想录的解法构建三个指针,i,left, right(可以用不同的方法 left mid right)。i从头开始遍历到最后(其实是length-2一定会截止).left = i+1;right = nums.length -1。left 和right从两边向中间靠拢。寻找和为0的组合。

去重:如果nums[i] = nums[i-1] 则调到下一个,因为i已经找过了,如果符合条件的话,那么后面的i就不需要了。
例如:[-1,-1,-1,2]
[-1,-1,2] --> [0,1,3]已经记录过,后面的[-1]就没有和leftright的组合就没有必要记录了(一定会重复)。所以调到下一个不同的nums[i]
还有就是leftright的去重
例如[-1,-1,-1,0,1,1,1]
[-1,0,1]-->[0,3,6] 此时left<right 所以还得继续进行,但是后面都是重复的所以需要去重。直到找到不同的leftright

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // 双指针:使用哈希的方法不大好,因为说了答案中不可以包含重复的三元组。
        // 重点是去重:去重的逻辑和细节很重要。
        // 三个指针,left, mid,right ,left 和right从左到右遍历,(left<mid<right),mid在中间遍历

        // 首先对数组里的元素进行排序,排完序之后才可以进行去重。
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        for(int i=0;i<nums.length-2;i++){
            if(nums[i]>0){
                return ans;
            }
            if(i > 0 && nums[i] == nums[i-1]){ // 如果已经计算过了,那么后面再出现 就要去重 例如[-1, -1, -1, 2], 已经记录了一个[-1, -1, 2]-->[0, 1, 3]。要是i=1时在记录一次就会重复。
                continue;

            }
            int left = i+1;
            int right = nums.length-1;
            while(right>left){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum>0){
                    right--;
                }
                else if(sum<0){
                    left++;
                }
                else{
                    ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    //  记录一个之后就要开始去重了[-1,-1,-1,0,1,1,1]
                    // 因为while还没到终止条件 记录的时刻是[-1, 0, 1]-->[0,3,6] 3<6.但是后面的不能要了。
                    while(left<right && nums[right] == nums[right -1]){
                        right--;
                    }
                    while(left<right && nums[left] == nums[left + 1]){
                        left++;
                    }
                    // 找到一个后及时移到下一位寻找新的数。
                    left++;
                    right--;
                }
            }
        }
        return ans;
    }
}
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> ans;
        sort(nums.begin(), nums.end());

        int length = nums.size();
        int right = length-1;
        int left = 1;
        for(int i=0;i<length-2;i++){
            if(nums[i] > 0){
                return ans;
            }


            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            left = i+1;
            right = length-1;
            while(right > left){
                if(nums[i] + nums[left] + nums[right] > 0){
                    right--;
                }
                else if(nums[i] + nums[left] + nums[right] < 0){
                    left++;
                }
                else{
                    ans.push_back(vector<int>{nums[i], nums[left], nums[right]});

                    // 去重逻辑
                    while(right > left && nums[right] == nums[right - 1]) right--;
                    while(right > left && nums[left+1] == nums[left]) left++;

                    // 找到一个且完成去重操作之后

                    right--;
                    left++;
                }
            }
        }
        return ans;
    }
};

LeetCode18:四数之和

思路:和三数之和类似,但是多了一层for循环,可以将前两个看成之前的i。
关键点:

if(nums[i] > 0 && nums[i] > target) { 
// 为了避免sum求和超出int范围变成负数的情况。
// 加上这个判断可以把相加之后超过int范围,变成负数的情况
// 排除[1000000000,1000000000,1000000000,1000000000] 
// target = -294967296。

                return ans;
            }

也可以直接将后面的换成long 就不会溢出了。

long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        // 同三数之和,不能重复。
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        for(int i=0;i<nums.length-3;i++){
            if (nums[i] > 0 && nums[i] > target) { // 
                return ans;
            }
            if(i>0 && nums[i-1] == nums[i]){ // 这里要i>0 也是首先在已经记录过的基础上才会去重,(同时i-1在起始位置 肯定会异常)。
                continue;
            }
            for(int j=i+1;j<nums.length-2;j++){
                if(nums[j-1] == nums[j]&& j>i+1){   // 此时j>i+1 因为j是在已经记录之后再进行剪枝去重的没所以肯定要>i+1。
                    continue;
                }
                int left = j+1;
                int right = nums.length-1;
                while(right > left){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if(sum<target){
                        left++;
                    }
                    else if(sum>target){
                        right--;
                    }
                    else{
                        ans.add(Arrays.asList(nums[i], nums[j], 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 ans;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值