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

89 阅读1分钟

454. 4Sum II

Given four integer arrays nums1nums2nums3, and nums4 all of length n, return the number of tuples (i, j, k, l) such that:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

 

Example 1:

Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
Output: 2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

Example 2:

Input: nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
Output: 1

 

Constraints:

  • n == nums1.length
  • n == nums2.length
  • n == nums3.length
  • n == nums4.length
  • 1 <= n <= 200
  • -228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228

今天的题目虽然看似都是X SUM,但输入数据和输出要求不一样,必须看清楚。这题的输入是4个等长数组,要求4个数组各出一个元素,其和等于0的组合的个数。

既然只要个数,那就可以用MAP存放前两个数组的两两元素之和为key,和出现的次数为value。 后两个元素同样操作。 得到两个MAP。 就可以扫第一个MAP,判断每个key在第二个MAP里是否有相反数为KEY的key-value组合。若是,count++

代码:

class Solution {
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        Map<Integer, Integer> mmap1 = new HashMap<Integer, Integer>();
        int count = 0;

        for(int i=0; i<nums1.length; i++) {
            for(int j=0; j<nums2.length; j++) {
                int sum = nums1[i] + nums2[j];

                if(mmap1.containsKey(sum)) {
                    int val = mmap1.get(sum);
                    val++;
                    mmap1.put(sum, val);
                }
                else {
                    mmap1.put(sum, 1);
                }
            }
        }

        for(int i=0; i<nums3.length; i++) {
            for(int j=0; j<nums4.length; j++) {
                int sum = -1 * (nums3[i] + nums4[j]) ;
                
                if(mmap1.containsKey(sum)) {
                    count += mmap1.get(sum);
                }

            }
        }

        return count;


    }
}

383. Ransom Note

Given two strings ransomNote and magazine, return true if ransomNote can be constructed by using the letters from magazine and false otherwise.

Each letter in magazine can only be used once in ransomNote.

 

Example 1:

Input: ransomNote = "a", magazine = "b"
Output: false

Example 2:

Input: ransomNote = "aa", magazine = "ab"
Output: false

Example 3:

Input: ransomNote = "aa", magazine = "aab"
Output: true

 

Constraints:

  • 1 <= ransomNote.length, magazine.length <= 105
  • ransomNote and magazine consist of lowercase English letters.

这题是Anagram那题的类似。都是小写字母,自定义字母map表即可。

class Solution {
    public boolean canConstruct(String ransomNote, String magazine) {
        int[] imap = new int[26];

        for(char c : magazine.toCharArray()) {
            imap[c-'a']++;
        }

        for(char c : ransomNote.toCharArray()) {
            imap[c-'a']--;
            if(imap[c-'a'] < 0) {
                return false;
            }
        }

        return true;


        
    }
}

15. 3Sum

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != ji != k, and j != k, and nums[i] + nums[j] + nums[k] == 0.

Notice that the solution set must not contain duplicate triplets.

 

Example 1:

Input: nums = [-1,0,1,2,-1,-4]
Output: [[-1,-1,2],[-1,0,1]]
Explanation: 
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0.
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0.
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0.
The distinct triplets are [-1,0,1] and [-1,-1,2].
Notice that the order of the output and the order of the triplets does not matter.

Example 2:

Input: nums = [0,1,1]
Output: []
Explanation: The only possible triplet does not sum up to 0.

Example 3:

Input: nums = [0,0,0]
Output: [[0,0,0]]
Explanation: The only possible triplet sums up to 0.

 

Constraints:

  • 3 <= nums.length <= 3000
  • -105 <= nums[i] <= 105

这题一开始只想到暴力解。虽然是3 SUM,但其实输入只有一个数组,要求数组内取三个元素,不能重复取,但可以取到重复的元素,使得三个元素的和为0. 这题用双指针,首先需要数组排序。然后方法是使用一个for loop遍历数组元素,最后两个不需要遍历。使用双指针left指向当前循环的下一个元素,right指向数组尾部元素。再开一个循环,终止条件为left < right。 循环内计算三个元素的和,若大于0,挪动right,若小于0,挪动left。 这题要注意剪枝和去重操作。 在for loop内,仅需遍历到倒数第三个元素。且当当前元素大于0时,就没必要继续遍历了,因为数组排过序了。直接返回结果list 去重的话,对三个指针都需要去重。 在for loop内,要注意不能检查num i == num i+1,因为这样会影响到接下来的left,left是可以取跟首元素一样的值的。因此必须num i == num i-1 && i>0 在while loop内,也要去重。要注意边界。去重完毕后要注意指针是指向最后一个重复的元素还是新的元素,若指向的是最后一个重复的元素位置,要额外挪动left和right

代码:

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

        for(int i=0; i<nums.length-2; i++) {
            if(nums[i] > 0) {
                return res;
            }

            int sum = 0;
            int left = i+1;
            int right = nums.length-1;

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

            while(left < right) {
                
                sum = nums[i] + nums[left] + nums[right];
                if(sum > 0) {
                    right--;
                    continue;
                }
                else if (sum < 0) {
                    left++;
                    continue;
                }
                else if (sum == 0) {
                    List<Integer> cur = new ArrayList<Integer>();
                    cur.add(nums[i]);
                    cur.add(nums[left]);
                    cur.add(nums[right]);

                    res.add(cur);
                    while(left < right && nums[left] == nums[left+1]) {
                        left++;
                    }
                    while(left < right && nums[right] == nums[right-1]) {
                        right--;
                    }

                    right--;
                    left++;

                }

            }
        }

        return res;
    }
}

18. 4Sum

Given an array nums of n integers, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:

  • 0 <= a, b, c, d < n
  • abc, and d are distinct.
  • nums[a] + nums[b] + nums[c] + nums[d] == target

You may return the answer in any order.

 

Example 1:

Input: nums = [1,0,-1,0,-2,2], target = 0
Output: [[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

Example 2:

Input: nums = [2,2,2,2,2], target = 8
Output: [[2,2,2,2]]

 

Constraints:

  • 1 <= nums.length <= 200
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

4 SUM 跟之前的3 SUM 类似,但要多一重循环。方法是先排序,然后二重嵌套循环数组来遍历前两个元素,再用双指针遍历后两个元素。

注意: 每层循环都需要包含去重。 另外剪枝要小心,因为target不是0.所以只能有限剪枝,当num i > 0 且num i> target时,可以剪枝。

代码:

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

        for(int i=0; i<nums.length - 3; i++) {
            if(nums[i] > 0 && nums[i] > target) {
                break;
            }
            if(i>0 && nums[i] == nums[i-1]) {
                continue;
            }
            for(int j=i+1; j<nums.length - 2; j++) {
                int sum2 = nums[i] + nums[j];
                if(nums[j] > 0 && sum2 > target) {
                    break;
                }
                if(j>i+1 && nums[j] == nums[j-1]) {
                    continue;
                }
                
                int left = j+1;
                int right = nums.length - 1;

                while(left < right) {
                    int sum4 = sum2 + nums[left] + nums[right];

                    if(sum4 > target) {
                        right--;
                        continue;
                    } 
                    else if (sum4 < target) {
                        left++;
                        continue;
                    }
                    else if (sum4 == target) {
                        List<Integer> cur = new ArrayList<Integer>();
                        cur.add(nums[i]);
                        cur.add(nums[j]);
                        cur.add(nums[left]);
                        cur.add(nums[right]);

                        res.add(cur);

                        while(left<right && nums[left] == nums[left+1]) left++;
                        while(left<right && nums[right] == nums[right-1]) right--;

                        left++;
                        right--;

                    }
                }


            }
        }

        return res;
    }
}