天天看点

力扣刷题思考——3数之和

1.三数之和

15. 三数之和

  • 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
  • 注意:答案中不可以包含重复的三元组。
    力扣刷题思考——3数之和

1.解题思路(迭代+快慢指针)

  • 0.先把数组进行升序排列,Arrays.sort(nums);
  • 1.建立3个指针,first、second、third,(以下使用first、second、third代替nums[first]、nums[second]、nums[third])可知,first <= second]<=third;
  • 2.如果要实现first+second+third=0,则有first<=0,且second+third=-1×first;
  • 3.题目要求:三元数组不可重复,所以first在每次for循环的时候需要枚举不同的值,second在每次for循环的时候需要枚举不同的值。
    • 1.首先对于first来说,eg.本次for循环,first=-1,那么second和third,会遍历所有以-1为第1个元素的三元数组组合(-1,second、third);
    • 2.然后对于second来说,eg:本次循环,first=-1,second=-1,那么指针third会从后向前移动,使得first + second + third = 0成立,在此过程中还要保证third指针严格在second指针的右侧,否则就continue;如果下次for循环,first=-1,second=0, 那么指针third还会从后向前移动,使得first + second + third = 0成立。
    • 所以可知,在相同的second时,third也是相同的,所以second的每次枚举值需要不同,才要保证third的取值不同;
    • 同理,每次first相同时,它所能匹配到的second+third组合也是相同的,所以first每次枚举值需要不同。

时间复杂度:O(n²)

空间复杂度:O(n)

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        if (nums==null || nums.length<3){
            return new ArrayList<>(0);
        }
        0.先进行数组排序
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();

        1.对第一层指针first进行遍历
        for (int first = 0; first < nums.length; first++) {

            1.first<=0, 因为如果first>0,那么就会导致 third > second > first > 0 从而无法三数之和=0
            if(nums[first]>0)
                break;
            2.每次first枚举值必须不同
            if (first>0 && nums[first]==nums[first-1]) continue;

            3.指针third从最后向前进行遍历,1次赋值即可,
             因为随着sencod的后移,third只会比前1个second所要求的更小才对,所以没必要每次second遍历,third都从最后nums.length-1向前开始遍历
            int third = nums.length-1;
            for (int second = first+1; second < nums.length; second++) {

                4.每次second指针枚举的元素必须都要不同
                if(second>first+1 && nums[second] == nums[second-1])
                    continue;
                5.在保证second在左 third在右的前提下,开始移动third
                while (second<third && nums[first]+nums[second]+nums[third]>0)
                    --third;

                6.如果指针重合,那就break,没必要再比下去了,后边的second都比当前second大, 所以根本找不到符合要求的third了
                if (second==third)
                    break;

                7.如果满足要求,就保存三元数组
                if (nums[first]+nums[second]+nums[third]==0){
                    res.add(Arrays.asList(nums[first],nums[second],nums[third]));
                }
            }
        }
        return res;
    }
}
           

2.解题思路(回溯算法)——但是超时

  • 1.数据结构的哈希表set,来保证三元数组的不可重复;
  • 2.要对数组进行Arrys.sort() 排序;
  • 3.回溯算法找出数组的所有三元子集,然后在此过程中计算三元数组的和是否为0;

时间复杂度:指数级

class Solution {
    Set<List<Integer>>  set;
    List<List<Integer>> res;

    public List<List<Integer>> threeSum(int[] nums) {
        if(nums==null&&nums.length<3){
            return new ArrayList<>();
        }
        Arrays.sort(nums);
        res = new ArrayList<>();
        List<Integer> list = new ArrayList<>();
        set = new HashSet<>();
        backTrack(nums,0,list,0);
        return res;
    }

    回溯算法
    public void backTrack(int[] nums, int index, List<Integer> path,int sum){
        1. 终止条件  数组长度为3 且 和为0 且 无重复
        if (path.size()==3 && sum==0 && set.add(new ArrayList<>(path))){
            res.add(new ArrayList<>(path));
            return;
        }
       2.终止条件 数组长度为3 或者 index是num.length
        if (path.size()==3 || index== nums.length)
            return;

        for (int i = index; i < nums.length; i++) {
            1.做选择
            sum += nums[i];
            path.add(nums[i]);
            当三元数组的第一个值大于0时,停止回溯,因为如果first>0,那么就会导致 third > second > first > 0 从而无法三数之和=0
            if (path.get(0)>0)
                return;
            2.回溯
            backTrack(nums, i+1, path, sum);

           3. 撤销选择
            sum -= nums[i];
            path.remove((Integer) nums[i]);
        }
    }
}