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

LeetCode 454.四数相加II

想法:看到本题的想法是将四数之和转化为两数之和进行计算,这样就能将四个循环降到两个循环,虽然有想法,但却不知道如何下手,在思考是否有更简单的解法(不敢相信是用两个for循环遍历),可能还是不够相信自己的判断,后来看了讲解文章,发现和自己的思路是一致的。代码如下:

import java.util.Map;
import java.util.HashMap;
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> record = new HashMap<Integer, Integer>();
        int count = 0;
        for(int i : nums1){
            for(int j : nums2){
                record.put(i + j, record.getOrDefault(i + j, 0) + 1);
            }
        }
        for(int k : nums3){
            for(int l : nums4){
                count += record.getOrDefault(-k-l, 0);
            }
        }
        return count;
    }
}

也是从卡尔的文章中学到的map的赋值处理,之前一直加if判断是否为null,原来java的map对象是有getOrDefault函数来进行初始的,又是学到知识的一天。

LeetCode 383.赎金信

想法:分别用两个Map对象来保存两个字符串中的字符及相应的出现次数,由于题目要求使用magazine中的字符来组成ransomNote,所以ransomNote中各字符的数量应小于等于magazine中该字符出现的次数。

代码如下:

import java.util.Map;
import java.util.HashMap;
class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        Map<String, Integer> record1 = new HashMap<String, Integer>();
        Map<String, Integer> record2 = new HashMap<String, Integer>();
        String[] s1 = ransomNote.split("");
        String[] s2 = magazine.split("");
        for(int i=0;i<s1.length; i++){
            record1.put(s1[i], record1.getOrDefault(s1[i], 0) + 1);
        }
        for(int i=0;i<s2.length; i++){
            record2.put(s2[i], record2.getOrDefault(s2[i], 0) + 1);
        }
        for(String key: record1.keySet()){
            // ransomNote中字符出现次数应小于等于magazine中改字符出现的次数
            if(record1.get(key) > record2.getOrDefault(key, 0)) return false;
        }
        return true;
    }
}

看了讲解文章,发现自己并没有关注到问题的关键点,即字符串中只有小写字母,对于这种取值范围有限制的情况下,一般用数组的耗时比map要小很多,所以这题可以采用和LeetCode 242.有效的字母异位词相同的解法,用一个长度为26的int数组来记录字符的出现次数,最后判断如果有负数的情况即说明magazine中的字母不足以组成字符串ransomNote,代码如下:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] record = new int[26];
        for(int i=0;i<magazine.length(); i++){
            record[magazine.charAt(i) - 'a']++;
        }
        for(int i=0;i<ransomNote.length(); i++){
            record[ransomNote.charAt(i) - 'a']--;
        }
        for(int count: record){
            // ransomNote中字符出现次数应小于等于magazine中改字符出现的次数
            if(count < 0) return false;
        }
        return true;
    }
}

LeetCode 15.三数之和

想法:想着类似于四数之和,用Map对象以前两数之和为key,前两数组成的数组为value进行遍历,然后再找第三个数。后来想想这样做的话对同一个数组遍历三次是否有必要,滑动窗口是否可以解决这个问题?如果用双指针的话,头和尾指针要怎么更新呢?最后还是没想到怎么做,看了讲解视频后大概知道要怎么做了。

思路:关键点在于要先对数组进行升序排序,这样后续找数以及去重会好处理许多。大体思路是用一个for循环来便利数组nums,其中i是第一个数的下标,left为第二个数下标,初始为i+1,right为第三个数下标,初始为nums.length-1(这里是双指针的精髓所在),判断三数之和是否等于0,若大于0,则rigth--,若小于0,则left++,依次遍历,该有一些去重的细节在代码中有体现:

import java.util.Arrays;
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);  // 对数组进行升序排序
        List<List<Integer>> result = new ArrayList<>();
        for(int i=0; i<nums.length; i++){
            if(nums[i] > 0) break;
            if(i>0 && nums[i] == nums[i-1]) continue; // nums[i]去重
            int left = i+1;
            int right = nums.length - 1;
            while(left < right){
                if(nums[i] + nums[left] + nums[right] > 0){
                    right--;
                }else if(nums[i] + nums[left] + nums[right] < 0){
                    left++;
                }else{
                    // List<Integer> temp = new ArrayList<>();
                    // temp.add(nums[i]);
                    // temp.add(nums[left]);
                    // temp.add(nums[right]);
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while(left < right && nums[left] == nums[left + 1]){ // nums[left]去重
                        left++;
                    }
                    while(left < right && nums[right] == nums[right-1]){ // nums[right]去重
                        right--;
                    }
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
}

其实在看视频以及文章时,我一直有个疑问,i的取值范围是[0, nums.length-1],而在for循环中,left=i+1,当i=nums.length-1时,数组不会越界吗?后来想想如果真到此时,right也等于nums.length-1,left大于right,是进不去循环的,也就不存在越界的情况,所以这里不需要做特殊的越界处理。

LeetCode 18.四数之和

与上题三数之和的解法类似,在双指针的基础上套两层for循环即可。本人在代码AC的过程中踩的坑:1.第二个循环j的去重,之前以为这里不需要去重了,但是提交说测试不通过,所以参考第一层循环i加上了去重的条件判断;2.缺少数组和target的边界剪枝处理,因为对数组进行了排序,当num[i]>0 && nums[i]>target时,说明第一个元素及后面的元素再怎么求和都会比target大,这个需要特殊处理一下。代码如下:

import java.util.Arrays;
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        for(int i=0;i<nums.length; i++){
            if(nums[i] > 0 && nums[i] > target) break;
            if(i>0 && nums[i] == nums[i-1]) continue;
            for(int j=i+1; j<nums.length;j++){
                if(j>i+1 && nums[j] == nums[j-1]) continue;
                int left = j+1;
                int right = nums.length -1;
                while(left < right){
                    if(nums[i] + nums[j] + nums[left] + nums[right] > target){
                        right--;
                    }else if(nums[i] + nums[j] + nums[left] + nums[right] < target){
                        left++;
                    }else{
                        result.add(Arrays.asList(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--;
                    }
                }
            }
        }
        return result;
    }
}

这篇博客与上篇博客隔了一周时间,这周有所懈怠,已经落后进度5天了,尽力追赶吧。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值