lc15.三数之和

lc15.三数之和_第1张图片

暴力解法:三层for循环,每个循环指向一个变量,求所有的和为零的情况

时间复杂度:O(n3)

空间复杂度:O(1)

双指针

1、对数组进行排序

2、外层循环控制第一个数 i;第一个数的范围必须保证小于等于0,若是第一个数大于零,后面的两个数都大于0,不可能实现三数和为零的情况

3、内层循环,控制第二个数 j 的指针初始为 i+1,从前向后遍历;第三个数为最后一个数k

初始为nums.length-1,从后向前遍历。此时计算nums[i]+nums[j]+nums[k]三数之和,将三数之和为0的i、j、k存入集合之中

4、同时存在有相等的数,当存在相同的数时,另一指针先不动,直到相同的数遍历完成为止

时间复杂度:O(n2)

空间复杂度:O(1)

import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class ThreeSum {

    @Test
    public void test() {
        int[] nums = new int[]{-1, 0, 1, 2, -1, -4};
        //[-4,-1,-1,0,1,2]
        int[] nums1 = new int[]{0, 0, 0};
        for (int i = 0; i < threeSum(nums1).size(); i++) {
            System.out.println(threeSum(nums1).get(i));
            //[-1,-1,2],[-1,0,1]
        }
    }

    public static List> threeSum(int[] nums) {
        List> result = new ArrayList<>();
        Arrays.sort(nums);
        int i = 0;
        while (nums[i] <= 0 && i <= nums.length - 3) {//小于倒数第二个数

            int j = i + 1, k = nums.length - 1;
            while (j < k) {
                int sum = nums[i] + nums[j] + nums[k];

                if (sum == 0 && !result.contains(Arrays.asList(nums[i], nums[j], nums[k]))) {
                    result.add(Arrays.asList(nums[i], nums[j], nums[k]));
                    //处理数字相等时的情况
                    //因为最后输出的nums数组中的值,不是索引,所以数字相同可以直接跳过
                    while (j < k && nums[j] == nums[j + 1]) {//j < k避免nums =[0,0,0]的情况下标越界
                        j++;
                    }
                    while (j < k && nums[k] == nums[k - 1]) {
                        k--;
                    }
                    //根据sum的值来调整j和k的位置
                } else if (sum < 0) {//sum<0说明三数之和较小,需要增大
                    j++;
                } else {//sum>0说明三数之和较大,需要减小
                    k--;
                }
            }
            i++;
        }
        return result;
    }
}

因为!result.contains(Arrays.asList(nums[i], nums[j], nums[k]))的判断在数据量大的时候判断相当占用时间,因此数据量大的时候会报超时

要想在不进行判断是否能够被存入数组,就需要保证在遍历的时候跳过i、j、k三个指针所有指向的相同的数据。因此All Perfect代码对第一、二、三个数都排除了相同数据的情况

反之,可以得出,在不排除相同的数据时,对于每一组数在进入集合前,都需要判断是否能够被存入集合。但contains一多整个代码跑的就慢

All Perfect代码

class Solution {
    public List> threeSum1(int[] nums) {
        List> res = new ArrayList<>();
        Arrays.sort(nums);
        for (int i = 0; i < nums.length; i++) {//第一个数i
            if (nums[i] > 0) {//大于0时退出循环
                break;
            }
            if (i > 0 && nums[i] == nums[i - 1]) {//如果相同,直接执行下一个
                //因为最后输出的nums数组中的值,不是索引,所以数字相同可以直接跳过
                continue;
            }
            int l = i + 1, r = nums.length - 1;
            while (l < r) {
                int sum = nums[i] + nums[l] + nums[r];
                if (sum == 0) {
                    res.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    //处理相同的数的情况
                    //因为最后输出的nums数组中的值,不是索引,所以数字相同可以直接跳过
                    while (l < r && nums[l] == nums[++l]) ;
                    while (l < r && nums[r] == nums[--r]) ;
                } else if (sum < 0) {
                    l++;
                } else {
                    r--;
                }
            }

        }
        return res;
    }
}

你可能感兴趣的:(算法,数据结构)