三数之和-四数之和

454.四数相加Ⅱ

题目链接/文章讲解/视频讲解:https://programmercarl.com/0454.%E5%9B%9B%E6%95%B0%E7%9B%B8%E5%8A%A0II.html

分析题目:从4个数组中各找一个元素相加等于0,相同元素可以重复使用

思路:我们要查找元素并且求和,那么我们就要考虑到哈希法,哈希法在本题中要如何使用,首先我们应该遍历前两个集合,找出其中所有nums1[i]+nums2[j]存入map集合中,key为和,val为和出现的次数,然后再遍历后两个集合,同样相加,再去集合中查找另外一半是否在集合中

public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
    Map<Integer,Integer> map = new HashMap<>();

    // 遍历前两个集合
    for(int i:nums1){
        for(int j: nums2){
            // 每个组合进行求和
            int sum = i+j;
            // 以(sum,sum出现的次数)存入map中,这里不需要去重
            // 一个数组中的一个元素可以使用多次,例如示例1中的nums3[0]
            // getOrDefault(键,默认值):存在键则取出里面的值,不存在则使用默认值
            map.put(sum,map.getOrDefault(sum,0)+1);
        }
    }

    int count=0;
    // 遍历后两个集合
    for(int i:nums3){
        for(int j:nums4){
            // 每个组合进行求和
            int sum = i+j;
            int num = 0-sum;
            // 根据计算的结果在map中查找,找到了则count进行计数
            if(map.containsKey(num)){
                count = map.get(num)+count;
            }
        }
    }

    return count;
}

383.赎金信

题目链接/文章讲解:https://programmercarl.com/0383.%E8%B5%8E%E9%87%91%E4%BF%A1.html

思路:这题的做法与有效字母异位词做法差不多,这里就不详细解释,代码中有注释

public boolean canConstruct(String ransomNote, String magazine) {
        
        if(ransomNote.length() > magazine.length()) return false;

        // 遍历ransomNote字符串,并存储到数组中
        int[] strArr = new int[26];
        for(int i=0; i<ransomNote.length(); i++){
            strArr[ransomNote.charAt(i)-97]++;
        }

        // 遍历ransomNote字符串,减去数组中存储的内容
        for(int i=0; i<magazine.length(); i++){
            strArr[magazine.charAt(i)-97]--;
        }

        // 只要数组中还存在字符串ransomNote的其他字符(大于0的值),则返回false
        for(int i:strArr){
            if(i > 0) {
                return false;
            }
        }

        return true;
    }

15.三数之和

题目链接/文章讲解/视频讲解:https://programmercarl.com/0015.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.html

题目分析:在一个数组中找出3个不同下标的元素相加和要等于0,不同的下标,那么我们就要提前考虑去重的问题

思路:这题可以使用双指针的做法,使用一个for循环来遍历数组,left指针指向i+1的位置,right指向数组最后一个元素,然后内部使用while循环,来根据3个下标中的值判断当前结果是否满足要求

去重逻辑:结合代码中的注释一起看
为什么代码中的 nums[i]==nums[i-1] 是i-1而不是i+1,举个例子[-1,-1,2],如果说当前i指向第一个-1时,如果条件为nums[i]==nums[i+1]就会忽略掉这个正确的结果集,后续left和right去重逻辑与注释中类似
注意:去重是去掉重复的三元组,而不是数组中重复的元素
例如:[ [1,2,-3], [2,-3,1] ],这种才是重复,而不是[-1,-1,2]内部元素重复

public List<List<Integer>> threeSum(int[] nums) {
    List<List<Integer>> result = new ArrayList<>();
	// 首先对数组进行排序
    Arrays.sort(nums);
    for(int i=0; i<nums.length; i++){
        if(nums[i]>0) return result;
        // nums[i]==nums[i-1]:对i进行去重,因为我们已经提前对数组进行了排序,
        // 如果当前元素等于我们前一个元素,那么就表示以这个元素开头的所有组合的结果我们已经全部收集了
        // 因为三元组不能重复,所以可以直接跳过这个元素,i>0是防止第一个元素索引越界
        if(i>0 && nums[i]==nums[i-1]) continue;

        int left = i+1;
        int right = nums.length-1;
        while(left < right){
            // 和大于0,我们就要缩短right,因为数组是排过序的
            if(nums[i]+nums[left]+nums[right] > 0){
                right--;
            }else if(nums[i]+nums[left]+nums[right] < 0){ // 同理
                left++;
            }else{
                result.add(Arrays.asList(nums[i],nums[left],nums[right]));
                // 必须在收集结果后,进行去重判断
                // 对left,right进行去重,逻辑同上,注意还是因为数组是有序的
                while(nums[right]==nums[right-1] && right>left) right--;
                // 如果left这里的元素一直重复,那么我们直接取最后一个left就可以了
                while(nums[left]==nums[left+1] && right>left) left++;

                // 当我们收集到一次结果后,left和right同时移动
                right--;
                left++;
            }  
        }
    }

    return result;

}

18.四数之和

题目链接/文章讲解/视频讲解:https://programmercarl.com/0018.%E5%9B%9B%E6%95%B0%E4%B9%8B%E5%92%8C.html

思路:与三数之和思路相同,只不过内部使用两个for进行遍历,唯一的区别就是三数之和要求是等于0,而四数之和是等于target,所以一些条件判断需要调整,大家就直接看代码和注释吧

List<List<Integer>> result = new ArrayList<>();

// 跟三数之和同样的思路,唯一需要注意的点就是对target值得条件判断
// 因为三数之和,求得是和等于0,而四数之和求得是和等于target

Arrays.sort(nums);
for(int i=0; i<nums.length; i++){
    // 如果第一个元素的值大于target那么就直接返回,前提是当前元素必须要大于0
    if(nums[i]>target && nums[i]>0) return result;
    // 对i去重
    if(i>0 && nums[i]==nums[i-1]) continue;
    for(int j=i+1; j<nums.length; j++){
        // 对j去重
        if(j>i+1 && nums[j]==nums[j-1]) continue;

        int left = j+1;
        int right = nums.length-1;
        while(left < right){
            // 注意:这里的sum是和target做比较,后面的思路与三数之和一样
            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(nums[right]==nums[right-1] && right>left) right--;
                while(nums[left]==nums[left+1] && right>left) left++;

                left++;
                right--;
            }
        }
    }
}
return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值