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

454.四数相加II

两数相加的变种

  1. 不要忘记map.getOrDefault()方法
class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int res = 0, sum, target;
        Map<Integer, Integer> record = new HashMap<>();


        for(int n1: nums1){
            for(int n2: nums2){
                sum = n1+n2;
                // map.put(sum, map.getOrDefault(sum, 0) + 1); 使用这个更好
                if(!record.containsKey(sum)) record.put(sum, 1);
                else{
                    record.put(sum, record.get(sum)+1);
                }
            }
        }
        for(int n3: nums3){
            for(int n4:nums4){
            	// 同上
                target = 0 - n3 - n4;
                if(record.containsKey(target)) res += record.get(target);
            }
        }

        return res;
    }
}

383. 赎金信

易错点:

  1. 边缘条件:不是长度不相等
  2. 同上,由于长度不相等,所以对两数组的遍历也不同。
  3. 使用str.toCharArray()方法,比str.charAt()更好。
class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int len1 = ransomNote.length(), len2 = magazine.length();
        if(len1>len2) return false;

        int[] record = new int[26];
        int i;
        for(i=0; i<len2; i++){
            if(i<len1) record[ransomNote.charAt(i)-'a']--;
            record[magazine.charAt(i)-'a']++;
        }

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

随想录解法:

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // shortcut
        if (ransomNote.length() > magazine.length()) {
            return false;
        }
        // 定义一个哈希映射数组
        int[] record = new int[26];

        // 遍历
        for(char c : magazine.toCharArray()){
            record[c - 'a'] += 1;
        }

        for(char c : ransomNote.toCharArray()){
            record[c - 'a'] -= 1;
        }
        
        // 如果数组中存在负数,说明ransomNote字符串中存在magazine中没有的字符
        for(int i : record){
            if(i < 0){
                return false;
            }
        }

        return true;
    }
}

15. 三数之和 (难)

这道题对我来说比较难,看了代码随想录才有思路。

  1. 双指针法:前提——数组排序(因为该题不需要返回下标)
  2. i 的去重:与前一个 - - - 与后一个 × ,因为需要添加到集合中。
  3. left 与 right 的去重:应该放入一个while循环进行去重。而且,比较的是left与left+1,right与right-1,这时没必要检查是否越界,同时也更好写一点。
  4. 循环条件:添加完结果之后,需要更新循环条件。
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();

        Arrays.sort(nums);
        int n = nums.length;
        for(int i=0; i<n-2; i++){
            // 边缘条件
            if(nums[i]>0) return res;

            // i的去重
            if(i>0 && nums[i]==nums[i-1]) continue;

            int left = i+1, right = n-1;
            while(left<right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum==0){
                	// 这里的去重逻辑有些笨拙
                    if (left>i+1 && nums[left]==nums[left-1]){
                        left++;
                        continue;
                    }
                    if (right<n-1 && nums[right]==nums[right+1]){ // left, right的去重
                        right--;
                        continue;
                    }   
                    res.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 不要忘记,添加res之后,需要更新循环条件
                    left++;
                    right--;
                } else if(sum>0){
                    right--;
                } else if(sum<0){
                    left++;
                }
            }
        }
        return res;
    }
}

代码随想录的循环:比我的更好。

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
	// 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.length; i++) {
	    // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) { 
                return result;
            }

            if (i > 0 && nums[i] == nums[i - 1]) {  // 去重a
                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 {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
		    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;
                    
                    right--; 
                    left++;
                }
            }
        }
        return result;
    }
}

18. 四数之和(需要注意小细节)

简单点:在三数之和的基础上,再套一层循环。
复杂点:很多处细节需要注意,与三数不同。

  1. 边缘条件(剪枝):
    if (nums[i] > 0)->if(nums[i]>=0 && nums[i]>target), if(nums[k] + nums[i]>=0 && nums[i]+nums[k]>target )
    并且,应该使用break,而非直接return。(只是剪枝)
    同时,目标不再是0,而是一个具体值target。
class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) { // 注意:是target,而不是0 
        List<List<Integer>> res = new ArrayList<>();

        Arrays.sort(nums);
        int n = nums.length;
        for(int i=0; i<n-3; i++){
            // 边缘条件:注意,不能那么简单
            if(nums[i]>=0 && nums[i]>target) return res; // 建议用break

            // i的去重
            if(i>0 && nums[i]==nums[i-1]) continue;

            for(int k=i+1; k<n-2; k++){
                // 边缘条件
                if(nums[k] + nums[i]>=0 && nums[i]+nums[k]>target ) break; // 不break而是return会有遗漏

                // k的去重
                if(k>i+1 && nums[k]==nums[k-1]) continue;
                
                int left = k+1, right = n-1;

                while(left<right){
                    int sum = nums[i] + nums[k] + nums[left] + nums[right];
                    if(sum>target){
                        right--;
                    }else if(sum<target){
                        left++;
                    }else{
                        res.add(Arrays.asList(nums[i], nums[k], nums[left], nums[right]));
                        while(left<right && nums[left]==nums[left+1]) left++;
                        while(left<right && nums[right]==nums[right-1]) right--;
                        right--;
                        left++;
                    }
                }

            }
        }
        return res;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值