[LeetCode] 3Sum

来源:互联网 发布:小乃海苔便当知乎 编辑:程序博客网 时间:2024/05/16 00:39

sum3问题:
对于给定的一组int,找到其中的三个数,这三个数相加为0。比如:
输入:
int[] nums= {-1 0 1 2 -1 -4}
输出:
(-1, 0, 1)
(-1, -1, 2)

第一个思路,最简单明了的,三循环暴力求解。。。时间复杂度当然是不敢恭维。。。

 public List<List<Integer>> threeSum(int[] nums) {        List<List<Integer>> list = new ArrayList<List<Integer>>();        int length = nums.length;        for(int i =0; i < length -2; i++){            int a = nums[i];            for(int j = i+1; j <length-1 ; j++){                int b = nums[j];                for(int k = j+1; k< length; k++){                    int c = nums[k];                    if(a+b+c == 0){                        ArrayList<Integer> item = new ArrayList<Integer>();                        item.add(a);                        item.add(b);                        item.add(c);                        Collections.sort(item);                        if(!list.contains(item)){                            list.add(item);                        }                    }                }            }        }        return list;

如果我们先进行排序,然后前面两个数循环,第三个数去二分查找,就可以得到一个O(n2*logn)的算法。
考虑下加法的特征,我们还可以稍微简化一下。
三个数相加等于0,则第一个数必然小于0,最后一个数必然大于0。
所以,如果我们取头尾的数,然后在中间进行二分查找,则还存在三个数学性质,可以简化判断。
第一个数大于0
最后一个数小于0
中间数小于第一个数或者大于最后一个数。
这样可以在同阶的情况下,稍微减少点循环数。

        //先排序,复杂度O(nlogn)        Arrays.sort(nums);        int length = nums.length;        List<List<Integer>> list = new ArrayList<List<Integer>>();        //两遍循环,进行查找。复杂度(n2*logn)        for(int i=0; i<length; i++){            int a = nums[i];            //第一个数大于0            if(a > 0){                break;            }            for(int j=length-1; j>1;j--){                int c = nums[j];                //第三个数小于0                if(c < 0){                    break;                }                int b = -(a+c);                //中间的数最小或者最大了                if(b < a || b > c){                    break;                }                if(contains(nums, b, i+1, j-1)){                    ArrayList<Integer> item = new ArrayList<Integer>();                    item.add(a);                    item.add(b);                    item.add(c);                    if(!list.contains(item)){                        list.add(item);                    }                }            }        }        return list;  //二分查找  public boolean contains(int[] nums, int a, int low, int high){           while(low <= high) {               int middle = (low + high)/2;               if(a == nums[middle]) {                   return true;               }else if(a <nums[middle]) {                   high = middle - 1;               }else {                   low = middle + 1;               }          }          return false;    }

反向思考,我们既然能先固定两头去查找中间的数,当然也能先固定中间的数,再去查找两头。尤其在计算了sum是否大于0后,我们可以确定遍历的方向。因此不需要漫无目的的遍历(0至i)*(i至length)了,而只需要遍历(0至length)即可。因此最后这个复杂度就下降到了i循环的O(n)以及查找low,high两者加一起的O(n)。时间复杂度为O(n2)。

        //先排序,复杂度O(nlogn)        Arrays.sort(nums);        int length = nums.length;        List<List<Integer>> list = new ArrayList<List<Integer>>();        //复杂度O(n2)        int low,high,sum;        for (int i = 1; i < length - 1; i++) {            low = 0;            high = length - 1;            while (low < i && high > i) {                sum = nums[i] + nums[low] + nums[high];                if (sum == 0) {                    ArrayList<Integer> item = new ArrayList<Integer>();                    item.add(nums[low]);                    item.add(nums[i]);                    item.add(nums[high]);                    if (!list.contains(item)) {                        list.add(item);                    }                    high--;                    low++;                } else if (sum > 0) {                    high--;                } else {                    low++;                }            }        }        return list;

对于2sum, 3sum closest, 4sum这三个问题,解题思路也都是一样的。
暴力查找最简单但复杂度最高。
利用排序,对最后一个数进行二分查找,可以把一个n降低到logn。
利用排序,对最后两个数进行加加减减的遍历,则可以由于方向的确定,把复杂度降低一个n阶。
最后附上一个4sum的代码实现:

public class Solution {    public List<List<Integer>> fourSum(int[] nums, int target) {        int length = nums.length;        List<List<Integer>> list = new ArrayList<List<Integer>>();        if(length < 4){            return list;        }        //排序        Arrays.sort(nums);        for(int i=0;i <length-3;i++){            for(int j=i+1; j<length-2;j++){                //先双循环                int sum = nums[i]+nums[j]-target;                int m =j+1;                int n =length-1;                while(m<n){                    //回到2sum的思路,按照result>或<0,进行++--                    int result =nums[m]+nums[n]+sum;                    if(result==0){                        ArrayList<Integer> item = new ArrayList<Integer>();                        item.add(nums[i]);                        item.add(nums[j]);                        item.add(nums[m]);                        item.add(nums[n]);                        if(!list.contains(item)){                            list.add(item);                        }                        m++;                        n--;                    }else if(result >0){                        n--;                    }else{                        m++;                    }                }            }        }        return list;    }}
0 0
原创粉丝点击