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

75 阅读3分钟

454.四数相加II

题目链接/文章讲解/视频讲解:programmercarl.com/0454.%E5%9B…

这是一道使用hashmap提高问题解决效率的题目。首先是用map存储前两个数组可能产生的所有和,需要注意的是,不同的数字可能会产生一样的和,所以需要存储每一种和出现的次数。然后再遍历后面两个数组产生的和。

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>();
        for (int n1 : nums1) {
            for (int n2 : nums2) {
                map.put(n1 + n2, map.getOrDefault(n1 + n2, 0) + 1);
            }
        }

        int cnt = 0;
        for (int n3 : nums3) {
            for (int n4 : nums4) {
                cnt += map.getOrDefault(-(n3 + n4), 0);
            }
        }

        return cnt;
    }
}

383. 赎金信

题目链接/文章讲解:programmercarl.com/0383.%E8%B5…

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        Map<Character, Integer> map = new HashMap<>();
        
        // 计算ransomNote中每个字符的出现次数
        for (int i = 0; i < ransomNote.length(); i++) {
            char c = ransomNote.charAt(i);
            map.put(c, map.getOrDefault(c, 0) + 1);
        }

        // 通过在magazine查找字符来减少映射中的计数
        for (int i = 0; i < magazine.length(); i++) {
            char c = magazine.charAt(i);
            if (map.containsKey(c)) {
                map.put(c, map.get(c) - 1);
                if (map.get(c) == 0) {
                    map.remove(c);
                }
            }
        }

        // 如果映射为空,则可以构造赎金信
        return map.isEmpty();
    }
}

15. 三数之和

题目链接/文章讲解/视频讲解:programmercarl.com/0015.%E4%B8…

这道题看似可以跟454相似,但是题目规定三个数不能想等,所以不能用454相似的哈希方法。除此之外,可以想到使用双指针。具体有4个关键点:

  1. 排序: 首先,对数组进行排序。排序是重要的一步,因为它使得使用双指针成为可能,并且有助于避免重复的三元组。

  2. 固定一个数,使用双指针找另外两个数:

    • 使用一个外层循环来固定一个数(称为nums[i])。这个循环遍历数组从开始到倒数第三个元素(因为需要至少三个数来形成三元组)。
    • 对于每个固定的数,使用两个指针(leftright)在剩下的数组部分中找另外两个数。left指针从固定数的右侧开始(i + 1),而right指针从数组的末尾开始。
  3. 移动双指针:

    • 对于每个固定的数,移动leftright指针直到它们相遇。
    • 如果三个数的和小于零,则移动left指针向右(增加和)。
    • 如果和大于零,则移动right指针向左(减少和)。
    • 如果和为零,将这三个数作为一个解添加到结果中,并移动两个指针寻找新的可能组合。
  4. 避免重复的三元组:

    • 在外层循环中,如果当前数字与前一个数字相同,则跳过此次循环,以避免重复的三元组。
    • 当找到和为0的三元组后,在移动leftright指针之前,跳过与当前leftright值相同的值,同样是为了避免重复。
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        for(int i = 0; i < nums.length - 2; i++){
            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){
                    left++;
                }else if(sum > 0){
                    right--;
                }else{
                    res.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--;
                }
            }
        }
        return res;
    }
}

18. 四数之和

题目链接/文章讲解/视频讲解:programmercarl.com/0018.%E5%9B…

这道题跟15三数之和相似,需要找到不重复的四元组,所以 可以在三数之和的基础上套一个for循环寻找四数之和。

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();

        for (int i = 0; i < nums.length - 3; i++) {
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            for (int j = i + 1; j < nums.length - 2; j++) {
                if (j > i + 1 && nums[j] == nums[j - 1]) {
                    continue;
                }
                int left = j + 1, right = nums.length - 1;
                while (left < right) {
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum < target) {
                        left++;
                    } else if (sum > target) {
                        right--;
                    } else {
                        res.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 res;
    }
}