DEV Community

Flame Chan
Flame Chan

Posted on

LeetCode Day6 HashTable Part2

LeetCode No.454 4Sum 2

Given four integer arrays nums1, nums2, nums3, 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

Original Page

A direct way to solve the problem but it will cause Time Limit Exceeded Because it is O(n^4) lol... crazy!

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int count = 0;
        for(int i:nums1){
            for(int j:nums2){
                for(int k: nums3){
                    for(int l: nums4){
                        if(i+j+k+l == 0){
                            count++;
                        }
                    }
                }
            }
        }
        return count;
    }
Enter fullscreen mode Exit fullscreen mode
    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        int count = 0;
        Map<Integer,Integer> map = new HashMap<>();

        for(int i:nums1){
            for(int j:nums2){
                // record the number of corrolation [1,1] [2,0] are the same 
                map.put(i+j,map.getOrDefault(i+j, 0) + 1);
            }
        }
        for(int i:nums3){
            for(int j : nums4){
                int target = 0 - i -j;
                count += map.getOrDefault(target, 0);
            }
        }
        return count;
    }
Enter fullscreen mode Exit fullscreen mode

time: O(n^2), space: O(n)
getOrDefault is a very useful function
I think I should pay some attention to Java's collection's function.


LeetCode 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
Orinigal Page

Image description

    public boolean canConstruct(String ransomNote, String magazine) {
        char[] ransomArr = ransomNote.toCharArray();
        char[] mgzArr = magazine.toCharArray();
        if(ransomArr.length > mgzArr.length){
            return false;
        }
        boolean notFound = false;
        for(int i=0; i<ransomArr.length; i++){
            int j=i; 
            notFound = true;
            for(int k=i; k<mgzArr.length; k++){
                if(ransomArr[i] == mgzArr[k]){
                    char temp = mgzArr[k];
                    mgzArr[k] = mgzArr[j];
                    mgzArr[j] = temp;  
                    notFound = false;
                    break;                  
                }
            }
            if(notFound){
                return false;
            }
        }
        return true;
    }
Enter fullscreen mode Exit fullscreen mode

The time complexity is O(n·m), space complexity is O(n+m)


LeetCode No.15 3Sum

Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != 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.
Original Page

Use a direct thought to write it first. But this is the wrong code

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

        List<List<Integer>> list = new ArrayList<>();
        for(int i = 0; i < nums.length - 2; i++) {
            for(int j = i + 1; j < nums.length - 1; j++) {
                for(int k = j + 1; k < nums.length; k++) {
                    if(nums[i] + nums[j] + nums[k] == 0) {
                        list.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    }
                }
            }
        }
        return list;

    }
Enter fullscreen mode Exit fullscreen mode

But this will cause some trouble containing duplicated result tuples e.g. [0,-1,1] and [-1,0,1] will be the same tuple in this question so we have to optimize the above code to achieve AC.

Using a double vector method may help because it is really difficult to conduct the problem that the array is not in-order as well as the we need to remove duplication

    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        if(nums[0] > 0){
            return new ArrayList<>();
        }
        List<List<Integer>> list = new ArrayList<>();
        for(int i = 0; i < nums.length - 2; i++) {
            if(nums[i] > 0){
                return list;
            }
            // nums[i] == nums[i-1] and nums[i] == nums[i+1] will lead to different result !!!!!
            if(i-1>=0 && nums[i] == nums[i-1]){
                continue;
            }

            int left = i+1;
            int right = nums.length-1;
            // left < right means left must less than nums.length-1 because max right is nums.length-1 and right will decrease
            while(left<right ){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    while(left<right && nums[right-1]==nums[right]){
                        right--;
                    }
                    right--;
                }else if(sum<0){
                    while(left<right && nums[left+1] == nums[left]){
                        left++;
                    }
                    left++;
                }else{
                    list.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while(left<right && nums[right-1]==nums[right]){
                        right--;
                    }
                    while(left<right && nums[left+1] == nums[left]){
                        left++;
                    }
                    right --;
                    left ++;
                }
            }
        }
        return list;      
    }
Enter fullscreen mode Exit fullscreen mode

the first version is not fine enough so I want to improve it

Image description
here we see that the inner while loop is for remove duplication for example [-1,-1,-1,-1,0,1,2,2,2,2]
if i=0 we have -1 so now left and right are -1 and 2 separately and the index of left and right is 1 and length-1 separately and our first result is [-1,-1,2].
but if we move the left to the right and move the right to the left
(index 0f left and right now is 2 and length-2) the result is the same result [-1,-1,2], and we do not want to see it. we want to cut these same elements down so we find left and right until get the different numbers.
This is the basic logic.
But we should be careful

  • the sum of them <0 means left is too small we only shift left to the right
  • the sum of them > 0 means the right is too large so we only move right to the left
  • =0 means we find the result and we should save the result now. (We have removed the i duplicated element by using another inner loop)

so here the 2 inner loops is redundant because whatever happens, the out loop will continue because the inner loop's work can be replaced by out loop

        Arrays.sort(nums);
        if(nums[0] > 0){
            return new ArrayList<>();
        }
        List<List<Integer>> list = new ArrayList<>();
        for(int i = 0; i < nums.length - 2; i++) {
            if(nums[i] > 0){
                return list;
            }
            // nums[i] == nums[i-1] and nums[i] == nums[i+1] will lead to different result !!!!!
            if(i-1>=0 && nums[i] == nums[i-1]){
                continue;
            }

            int left = i+1;
            int right = nums.length-1;
            // left < right means left must less than nums.length-1 because max right is nums.length-1 and right will decrease
            while(left<right ){
                int sum = nums[i] + nums[left] + nums[right];
                if(sum > 0){
                    right--;
                }else if(sum<0){
                    left++;
                }
                else{
                    list.add(Arrays.asList(nums[i], nums[left], nums[right]));
                    while(left<right && nums[right-1]==nums[right]){
                        right--;
                    }
                    while(left<right && nums[left+1] == nums[left]){
                        left++;
                    }
                    right--;
                    left++;
                }
            }
        }
        return list;      
    }
Enter fullscreen mode Exit fullscreen mode

time: O(n^+nlogn)[sort: nlogn, other n^2], space: O(1) no extra space

Top comments (0)