zoukankan      html  css  js  c++  java
  • [LeetCode#259] 3Sum Smaller

    Problem:

    Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target.

    For example, given nums = [-2, 0, 1, 3], and target = 2.

    Return 2. Because there are two triplets which sums are less than 2:

    [-2, 0, 1]
    [-2, 0, 3]
    

    Follow up:
    Could you solve it in O(n2) runtime?

    General Analysis:

    This problem is easy, but really tricky!
    It could force you to consider many detail you have not consider before.
    
    First, we need to understand it is difference with 3Sum problem.
    difference 1: the nums may still have duplicates, but we don't need to care about it, since the problme ask us to "find the number of index triplets i, j", which means as long as the elements are different(not value), we could record it.
    
    difference 2: it asks us to find all combinations smaller than "target", which is not the same as "equal", the way of couting answer should be different. 
    
    I have made following mistakes to figure out the solution:

    Wrong Solution 1:

    public class Solution {
        public int threeSumSmaller(int[] nums, int target) {
            if (nums == null)
                throw new IllegalArgumentException("nums is null");
            List<Integer> ret = new ArrayList<Integer> ();
            ret.add(0);
            Arrays.sort(nums);
            for (int i = 0; i < nums.length - 2; i++) {
                twoSumSmaller(nums, i + 1, target - nums[i], ret);
            }
            return ret.get(0);
        }
        
        private void twoSumSmaller(int[] nums, int i, int target, List<Integer> ret) {
            int front = i;
            int end = nums.length - 1;
            while (front < end) {
                if (nums[front] + nums[end] < target) {
                    ret.set(0, ret.get(0) + 1);
                    end--;
                } else{
                    end--;
                }
            }
        }
    }

    Mistakes Analysis:

    Error cases:
    Input:
    [3,1,0,-2], 4
    Output:
    2
    Expected:
    3
    
    The above solution almost share the same code structure with 3Sum problem, excpet the condition to add "item" into "ret" list. And we don't need to care about duplicates things.
    However, the above logic of shirking the available range is wrong for this problem.
    Recall, for 3Sum Problem, we have,
    ---------------------------------------------------
    if (nums[front] + nums[end] == target) {
        ...
        front++ (end--); <suppose we have no duplicates>
    }
    ---------------------------------------------------
    We can move both front and end pointers. Since "nums[front] + nums[end] == target", whether we move front or end pointer, 
    iff nums[front_new] != nums[front] (which is guaranteed), we still need to move end pointer. Thus, we actually need to move both pointers for possible available ranges, no possible answer would be skipped.
    
    But for this problem, it has great difference.
    Problemetic part:
    if (nums[front] + nums[end] < target) {
        ...
        end--;
    }
    
    For above code, the end may still in available range, but we discard it, thus we lose some answers.
    "nums[front+1] + nums[end] < target"  may be possible. 
    Fix:
    if (nums[front] + nums[end] < target) {
        ...
        front++;
    }

    Wrong Solution 2:

    public class Solution {
        public int threeSumSmaller(int[] nums, int target) {
            if (nums == null)
                throw new IllegalArgumentException("nums is null");
            List<Integer> ret = new ArrayList<Integer> ();
            ret.add(0);
            Arrays.sort(nums);
            for (int i = 0; i < nums.length - 2; i++) {
                twoSumSmaller(nums, i + 1, target - nums[i], ret);
            }
            return ret.get(0);
        }
        
        private void twoSumSmaller(int[] nums, int i, int target, List<Integer> ret) {
            int front = i;
            int end = nums.length - 1;
            while (front < end) {
                if (nums[front] + nums[end] < target) {
                    ret.set(0, ret.get(0) + 1);
                    front++;
                } else{
                    end--;
                }
            }
        }
    }

    Mistakes Analysis:

    Error cases:
    Input:
    [3,1,0,-2], 4
    Output:
    2
    Expected:
    3
    
    Even though we fix the problem for moving pointer of this problem, we still face the other important problem.
    The problem with above solution is that: use the same counting method of 3Sum.
    
    Problemetic part:
    if (nums[front] + nums[end] < target) {
        ret.set(0, ret.get(0) + 1);
        front++;
    } 
    
    If "nums[front] + nums[end] < target" is meet, all numbers from {nums[front] to nums[end-1]} must also meet the answer, with front fixed. thus we should have following counting method.
    ret.set(0, ret.get(0) + end - front);
    
    You may wander wheather it would incure duplicates in counting.
    Nope! since after we move front++, for ceratin "nums[i]", the same "nums[front]" would not appear again. 

    Analysis:

    Ask yourself:
    This problem actually shares a lot of things with 3Sum. Like "binary search problem", this also represents a new genre of problem.  Taking part of a sorting array. With one element was slected, 
    for (int i = 0; i < nums.length - 2; i++) {
        twoSumSmaller(nums, i + 1, target - nums[i], ret);
    }
    How could you use the invariant "nums[front] + nums[end] meets certain condition" to control the searching in available ranges and couting right answer!!!
    
    It's an art!!! Right!

    Solution:

    public class Solution {
        public int threeSumSmaller(int[] nums, int target) {
            if (nums == null)
                throw new IllegalArgumentException("nums is null");
            List<Integer> ret = new ArrayList<Integer> ();
            ret.add(0);
            Arrays.sort(nums);
            for (int i = 0; i < nums.length - 2; i++) {
                twoSumSmaller(nums, i + 1, target - nums[i], ret);
            }
            return ret.get(0);
        }
        
        private void twoSumSmaller(int[] nums, int i, int target, List<Integer> ret) {
            int front = i;
            int end = nums.length - 1;
            while (front < end) {
                if (nums[front] + nums[end] < target) {
                    ret.set(0, ret.get(0) + end - front);
                    front++;
                } else{
                    end--;
                }
            }
        }
    }
  • 相关阅读:
    OD调试1--第一个win32程序
    Koa与Node.js开发实战(1)——Koa安装搭建(视频演示)
    《11招玩转网络安全》之第五招:DVWA命令注入
    《11招玩转网络安全》之第四招:low级别的DVWA SQL注入
    一张图11招学会Python网络黑客
    《11招玩转网络安全》之第三招:Web暴力破解-Low级别
    《11招玩转网络安全》之第二招:漏洞扫描
    《11招玩转网络安全》之第一招:Docker For Docker
    11招玩转黑客攻防——用Python,更安全
    如何有效的练习并且提升写代码的能力?
  • 原文地址:https://www.cnblogs.com/airwindow/p/4802521.html
Copyright © 2011-2022 走看看