力扣刷题Day6

454. 四数相加

题目:力扣

这道题因为没有考虑重复元素问题,因此可以考虑简单一些。

可以将数组分为两组。

A + B + C + D = 0 ==> A+B = 0 -(C+D)

用map储存A+B「key」以及出现频率「value」,然后用C+D查找是否存在A+B - 累加频率

代码实现:

 public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i=0; i<nums1.length; i++){
            for(int j=0; j<nums2.length; j++){
                int sum = nums1[i]+nums2[j];

                map.put(sum, map.getOrDefault(sum, 0)+1);
            }
        }

        int result = 0;
        for(int i=0; i<nums3.length; i++){
            for(int j=0; j<nums4.length; j++){
                int target = nums3[i]+nums4[j];

                if(map.containsKey(0-target)){
                    result+= map.get(0-target);
                }
            }
        }

        return result;
    }

 参考资料:代码随想录

383. 赎金信

题目:力扣

解法其实和有效的分母异位词类似,使用hashmap,这里因为明确是使用magazine 来生成ransom,因此需要储存进map的字符以及频率就必须是magazine。

然后遍历ransom,如果有在map中不存在的字符则直接返回false;有存在的字符则将频率-1 ==》代表被使用;如果频率小于0,则返回false。

    public boolean canConstruct(String ransomNote, String magazine) {
        HashMap<Character, Integer> map = new HashMap<>();
        for(int i=0; i<magazine.length(); i++){
            map.put(magazine.charAt(i), map.getOrDefault(magazine.charAt(i),0)+1);
        }

        for(int i=0; i<ransomNote.length(); i++){
            if(map.containsKey(ransomNote.charAt(i))){
                map.put(ransomNote.charAt(i), map.get(ransomNote.charAt(i))-1);
                if(map.get(ransomNote.charAt(i)) < 0) return false;
            }else{
                return false;
            }

        }

        return true;

    }

15. 三数之和 

 第一反应是先排序;然后使用双指针,固定第一位,然后用left和right指向数组剩下的头和尾;若是和为0则保存数列;若是大于0则right向左移动 - 减小;若是小于0则left向右移动 - 增加。

整体逻辑很简单,但是注意细节:

因为需要返回不重复的list,那么就需要去重。

如何去重?

第一种方案是采用hashset - 这里注意使用Stream进行集合类型转换

public List<List<Integer>> threeSum(int[] nums) {

        if(nums == null || nums.length < 3){
            return null;
        }
        
        Arrays.sort(nums);

        HashSet<List<Integer>> set = new HashSet<>();

        for(int i=0; i<nums.length-2; i++){
            
            if(nums[i] > 0) break; //剪枝
            
            int left = i+1;
            int right = nums.length -1;
            int sum = nums[i] + nums[left] + nums[right];

            

            while(left < right){
                if(sum == 0){
                    set.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    left++;
                    right--;
                }else if(sum > 0){
                    right --;
                }else{
                    left++;
                }

                sum = nums[i] + nums[left] + nums[right];
            }
        }
        
        return set.stream().toList();

        // List<List<Integer>> result = new ArrayList<>();
        // for(List<Integer> l : set){
        //     result.add(l);
        // }

        // return result;
    }

第二种方案是通过比较指针移动的数是否相同 - 这里有个细节问题不能讲三元素中存在重复元素的list忽略

也就是这个写法存在问题:

if (nums[i] == nums[i + 1]) { // 去重操作
    continue;
}

例如{-1, -1 ,2} 这组数据,当遍历到第一个-1 的时候,判断 下一个也是-1,那这组数据就pass了。 

应该这样写:

if (i > 0 && nums[i] == nums[i - 1]) {
    continue;
}

 当前使用 nums[i],我们判断前一位是不是一样的元素,在看 {-1, -1 ,2} 这组数据,当遍历到 第一个 -1 的时候,只要前一位没有-1,那么 {-1, -1 ,2} 这组数据一样可以收录到 结果集里。

具体代码实现:

 public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if(nums == null || nums.length < 3){
            return result;
        }
        
        Arrays.sort(nums);
        for(int i = 0; i < nums.length; i++){
            //第一个数大于0,那么说明三数和肯定大于0
            if(nums[i] > 0){
                break;
            }
            //如何去重
            if(i>0 && nums[i] == nums[i-1]){
                continue;
            }
            int left = i+1;
            int right = nums.length -1;
            while(left < right){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum == 0){
                    result.add(Arrays.asList(nums[i],nums[left],nums[right]));
                    //去重
                    while(left < right && nums[left] == nums[left+1]){
                        left++;
                    }
                    while(left < right && nums[right] == nums[right-1]){
                        right--;
                    }
                    left++;
                    right--;

                }else if(sum < 0){
                    left++;
                }else{
                    right--;
                }
            }
        }

        return result;

    }

 参考资料:代码随想录

 18. 四数之和

 逻辑和三数之和是一样的,但是细节上需要注意:

  • 剪枝问题 -  不要判断nums[k] > target 就返回了, 四数之和这道题目 target是任意值。比如:数组是[-4, -3, -2, -1]target-10,不能因为-4 > -10而跳过。但是我们依旧可以去做剪枝,逻辑变成nums[i] > target && (nums[i] >=0 || target >= 0)就可以了。
public List<List<Integer>> fourSum(int[] nums, int target) {
        if(nums == null || nums.length <4) return new ArrayList<>();

        Arrays.sort(nums);
        HashSet<List<Integer>> set = new HashSet<>();

        for(int i=0; i<nums.length -3; i++){

            //剪枝操作
            if(nums[i] > target && nums[i] > 0) break;
            for(int j=i+1; j<nums.length -2; j++){

               // if(nums[i] + nums[j] > target) continue;

                int left = j+1;
                int right = nums.length -1;
                int sum = nums[i] + nums[j] + nums[left] +nums[right];

                while(left < right){
                    if(sum == target){
                        set.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                        left++;
                        right --;
                    }else if(sum > target){
                        right --;
                    }else{
                        left++;
                    }

                    sum = nums[i] + nums[j] + nums[left] +nums[right];
                }
            }
        }

        return set.stream().toList();

    }

参考资料:代码随想录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值