Thursday, March 30, 2017

15. 3Sum

四刷 06/2022
Version #1 Two Pointers
一个可以优化但是没写出来的点就是因为array是sorted,所以如果outer loop的nums[i] > 0就可以break了,因为三个 >0 的数是没法加成0的
这里犯了一个错误就是
Arrays.asList(new int[]{a, b})这样创造出来的ArrayList是immutable的,如果想让它mutable就要先赋值给一个 new ArrayList<>(Arrays.asList(a, b))
同时看了之前的写法,也可以直接把result list的reference传进去,然后2sum找到结果以后直接加到结果里面
Time O(N^2)
Space O(logN) to O(N) used by the sorting algorithm

Runtime: 37 ms, faster than 46.23% of Java online submissions for 3Sum.
Memory Usage: 59.8 MB, less than 32.09% of Java online submissions for 3Sum.
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            if (i != 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            List<List<Integer>> pairs = twoSum(nums, i + 1, nums.length - 1, -nums[i]);
            for (List<Integer> pair : pairs) {
                pair.add(nums[i]);
                result.add(pair);
            }
        }
        return result;
    }
    
    // Find distinct pair that add up to target from nums[start, end]
    private List<List<Integer>> twoSum(int[] nums, int start, int end, int target) {
        List<List<Integer>> result = new ArrayList<>();
        while (start < end) {
            int sum = nums[start] + nums[end];
            if (sum == target) {
               result.add(new ArrayList<>(Arrays.asList(nums[start], nums[end])));
                start++;
                while (start < end && nums[start] == nums[start - 1]) {
                    start++;
                }
            } else if (sum < target) {
                start++;
            } else {
                end--;
            }      
        }
        return result;
    }
}

Version #2 HashSet - no sorting
Time O(N^2)
Space O(N)
Runtime: 684 ms, faster than 14.39% of Java online submissions for 3Sum.
Memory Usage: 150.5 MB, less than 6.74% of Java online submissions for 3Sum.
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Set<List<Integer>> result = new HashSet<>();
        Set<Integer> visited = new HashSet<>();
        for (int i = 0; i < nums.length; i++) {
            if (visited.contains(nums[i])) {
                continue;
            }
            visited.add(nums[i]);
            Set<Integer> seen = new HashSet<>();
            for (int j = i + 1; j < nums.length; j++) {
                if (seen.contains(-nums[i] - nums[j])) {
                    List<Integer> tuple = Arrays.asList(nums[i], nums[j], -nums[i] - nums[j]);
                    Collections.sort(tuple);
                    result.add(tuple);
                }
                seen.add(nums[j]);
            }
        }
        return new ArrayList<>(result);
    }
}

Input:[0,0,0,0]
Output:[[0,0,0],[0,0,0]]
Expected:[[0,0,0]]


三刷 05/2022
一遍bug free过了,开心
重点是要通过sorted array 来deduplicate
这里稍微陷入了一个错误,就是以为如果 i, j, k在sorted array里面,那一定只能是两个小的值加起来得到一个大的值。其实这是不对的
nums[i] + nums[j] + nums[k] = 0,无论把哪个数放到右边都成立
所以outer loop不一定是i = nums.length - 1, i--,如果用i = 0, i < nums.length也是正确的
Runtime: 25 ms, faster than 82.71% of Java online submissions for 3Sum.
Memory Usage: 58.7 MB, less than 59.10% of Java online submissions for 3Sum.
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        // Make it a two sum question
        // nums[i] + nums[j] == nums[k]
        // Since we want to deduplicate the solution set, we need to sort the array first and skip numbers when index != 0 && nums[index] == nums[index - 1]
        if (nums == null || nums.length < 2) {
            return new ArrayList<>();
        }
        Arrays.sort(nums);
        List<List<Integer>> result = new ArrayList<>();
        for (int i = nums.length - 1; i >= 0; i--) {
            if (i != nums.length - 1 && nums[i] == nums[i + 1]) {
                continue;
            }
            twoSum(nums, i, result);
        }
        return result;
    }
    // Given nums[startIndex], find all pairs of numbers on its left that sum up to its value
    private void twoSum(int[] nums, int endIndex, List<List<Integer>> result) {
        int target = -nums[endIndex];
        int left = 0, right = endIndex - 1;
        while (left < right) {
            if (nums[left] + nums[right] < target) {
                left++;
                continue;
            }
            if (nums[left]  + nums[right] > target) {
                right--;
                continue;
            }
            result.add(Arrays.asList(nums[left], nums[right], nums[endIndex]));
            left++;
            right--;
            while (left < right && nums[left] == nums[left - 1]) {
                left++;
            }
            while (left < right && nums[right] == nums[right + 1]) {
                right--;
            }
        }
    }
}


二刷
还是写的很艰难
对于一样的数的处理是跳过,left不能和left+1一样,right不能和right-1一样,但是left是可以和right一样的
有空还是再写写吧
result.add(Arrays.asList(nums[targetIndex], nums[left], nums[right]));
这种写法也要熟悉
79.71 %

public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length <= 2) return result;
        Arrays.sort(nums);
        //TODO
        for (int i = 0; i < nums.length - 2; i++) {
            if (i == 0 || nums[i] != nums[i - 1]) {
                twoSum(nums, i, result);
            }
        }
        return result;
    }
    private void twoSum(int[] nums, int targetIndex, List<List<Integer>> result) {
     
        int target = 0 - nums[targetIndex];
        int left = targetIndex + 1, right = nums.length - 1;
        while (left < right) {
            if (nums[left] + nums[right] == target) {
                result.add(Arrays.asList(nums[targetIndex], 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 (nums[left] + nums[right] > target) {
                right--;
            } else {
                left++;
            }
        }
    }
}

Version #1 Two Pointers
Time O(n^2)
三刷
三个数当遇到duplicate的时候都要跳

36.43 %
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length < 3) {
            return result;
        }
        Arrays.sort(nums);
        int len = nums.length;
        for (int i = 0; i < len; i++) {
            if (i != 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = len - 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]));
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                } else if (sum < 0) {
                    left++;
                    while (left < right && nums[left] == nums[left - 1]) {
                        left++;
                    }
                } else {
                    right--;
                    while (left < right && nums[right] == nums[right + 1]) {
                        right--;
                    }
                }
            }
        }
        return result;
    }
}


一刷


Given an array S of n integers, are there elements abc in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
 Notice
Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
53.69%
做这道题出现了很多小bug,主要问题是在用pointer的时候,while loop是不能无脑加的
每一次都要判断pointer是否出界
判断依据譬如left < right, index > 0, index < numbers.length等等
TwoSum可以单独写成一个method
Time Complexity O(n^2) Space Complexity O(1)
public class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        if (nums == null || nums.length == 0) return result;
        Arrays.sort(nums);
        int left, right;
        int index = 0;
        while (index < nums.length - 2) {
            if (index > 0 && nums[index] == nums[index - 1]) {
                index++;
                continue;
            }
            int target = nums[index];
            left = index + 1;
            right = nums.length - 1;
            while (left < right) {
                if (nums[left] + nums[right] + target == 0) {
                    List<Integer> temp = new ArrayList<>();
                    temp.add(target);
                    temp.add(nums[left]);
                    temp.add(nums[right]);
                    result.add(temp);
                    left++;
                    right--;
                    while (left < right && nums[left] == nums[left - 1]) left++;
                    while (left < right && nums[right] == nums[right + 1]) right--;
                } else if (nums[left] + nums[right] + target < 0) {
                    left++;
                } else {
                    right--;
                }
            }
            index++;
        }
        return result;
    }
}

No comments:

Post a Comment