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

Leetcode 454.四数相加II

题目链接
思路:求四数相加之和,将四数两两相加,判断两两相加的数是否和为0

  1. 定义一个map,key放两数之和,value放两数之和出现的次数
  2. 两层for循环将前两个数组的每两个元素之和放入map中
  3. 两层for循环判断后两个数组的每两个元素之和的相对值是否在map中,如果在map中,将count值相加,即为四数之和为0的次数

代码

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> map = new HashMap<>();
        int tempSum;
        int count = 0;
        // 统计两个数组中的元素之和,同时统计出现的次数,放入map
        for (int i : nums1) {
            for (int j : nums2) {
                tempSum = i + j;
                map.put(tempSum, map.getOrDefault(tempSum, 0) + 1);
            }
        }
        // 统计剩余的两个元素的和,在map中找是否存在相加为0的情况,同时记录次数
        for (int i : nums3) {
            for (int j : nums4) {
                tempSum = i + j;
                if (map.containsKey(-tempSum)) {
                    count += map.get(-tempSum);
                }
            }
        }
        return count;
    }
}

Leetcode 383. 赎金信

题目链接
思路:遍历magazine,将magazine中元素出现的次数放入哈希表中,然后遍历ransomNote,将ransomNote中元素的出现的次数不断在哈希表中进行减一,最后判断哈希表中的元素是否有负值。
代码

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        // 定义一个哈希表,用来存放26个英文字母
        int[] record = new int[26];
        // 遍历magazine,将magazine中的每个字符放入record中
        for (char c : magazine.toCharArray()) {
            record[c - 'a'] += 1;
        }
        // 遍历ransomNote,将ransomNote中的每个字符从record中进行减一
        for (char c : ransomNote.toCharArray()) {
            record[c - 'a'] -= 1;
        }
        // 对record进行遍历,如果小于0,则说明ransomNote不能由magazine中的字符组成,返回false
        for (int i : record) {
            if (i < 0) {
                return false;
            }
        }
        return true;
    }
}

Leetcode 15. 三数之和

题目链接
思路:采用双指针法,首先将数组进行排序,然后遍历数组,定义一个left指针,令其指向i+1的位置,定义一个right指针,令其指针指向数组的末尾位置,如果nums[i] + nums[left] + nums[right] >0,则向左移动right指针,使其数值减小,如果nums[i] + nums[left] + nums[right]<0,则向右移动left指针,使其数值变大,这里有很多要去重的细节,下面直接看代码。
代码

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], i为遍历的元素, left为i+1, right为数组末尾元素
        for (int i = 0; i < nums.length; i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回
            if (nums[i] > 0) {
                return result;
            }
            // 对a进行去重
            // 去重a的意思是不能有重复的三元组,但三元组内的元素是可以重复的!
            // 例如:在{-1, -1 ,2}这组数据,当遍历到第一个-1的时候,只要前一位没有-1,那么{-1, -1, 2}这组数据一样可以收录到 结果集里。
            // 但是如果判断nums[i] == nums[i + 1],则是三元组内不能有重复元素
            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) {
                    right--;
                } else if (sum < 0) {
                    left++;
                } else {
                    result.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    // 这里去重b和c,去重逻辑应该放在找到一个三元组之后
                    // 例如:在{0, 0, 0}这组数据中,如果在找到一个三元组之前就去重,那么则无论如何都不存在这组{0, 0, 0}的数据
                    while (right > left && nums[right] == nums[right - 1]) {
                        right--;
                    }
                    while (right > left && nums[left] == nums[left + 1]) {
                        left++;
                    }
                    right--;
                    left++;
                }
            }
        }
        return result;
    }
}

Leetcod 18. 四数之和

题目链接
思路:这里四数之和相当于上题三数之和套了一层for循环,同样适用双指针法,并且第一层for循环也需要进行剪枝和去重操作,在理解三数之和的基础上在处理四数之和就变得简单了。
代码

    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) {
                    return result;
                }
                // 对nums[i]去重
                if (i > 0 && nums[i - 1] == nums[i]) {
                    continue;
                }

                for (int j = i + 1; j < nums.length; j++) {
                    // 对nums[j]去重
                    if (j > i + 1 && nums[j - 1] == nums[j]) {
                        continue;
                    }

                    int left = j + 1;
                    int right = nums.length - 1;
                    while (right > left) {
                        // nums[k] + nums[i] + nums[left] + nums[right] > target int会溢出
                        long sum = (long) nums[i] + nums[j] + nums[left] + nums[right];
                        if (sum > target) {
                            right--;
                        } else if (sum < target) {
                            left++;
                        } else {
                            result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));
                            // 对nums[left]和nums[right]去重
                            while (right > left && nums[right] == nums[right - 1]) {
                                right--;
                            }
                            while (right > left && nums[left] == nums[left + 1]) {
                                left++;
                            }
                            left++;
                            right--;
                        }
                    }
                }
            }
            return result;
        }
    }

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode,数据结构,java,散列表)