力扣No.15——求三数之和

题目

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]

示例 2:
输入:nums = []
输出:[]

示例 3:
输入:nums = [0]
输出:[]

提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105

解题步骤:

核心逻辑:

初次看到此题时,三数之和为0,分为三种情况:两负一正,两正一负,还有三个0。
因此我们可以设置目标数组的每一个元素为每一轮循环的标记位置now,然后它后面的元素为左边指针left的起始点,目标数组的最后一个元素为右边指针的起始点right,然后通过判断三者对应的值,来控制指针的移动。
image-20211021153756650

如果三数之和<0,移动左指针。

image-20211021154025254

如果三数之和=0,则进行判重,将左右指针分别移动到与当前指针不相同的位置。

image-20211021153959688

如果三数之和>0,则移动右指针。

image-20211021154011284

特殊值判断:

  1. 三数之和,则长度小于3项则直接淘汰。
  2. 标记位置now对应的元素是三个数中最小的一个元素,要么是负数,要么是0,所以标记元素大于0则直接淘汰。
  3. 如果当前标记位置now对应的元素与now-1位置对应的元素相等,则直接跳过当前轮次,去重。
  4. 左指针
    1. 因为左指针指向的位置比now的位置大1,因此要进行边界值的判断。即now最大取length-1。 初始:left=now+1;
    2. 左指针要始终比右指针小 left<right

代码展示

1.排序+双指针(最优解)

 public static List<List<Integer>> threeSum(int[] nums) {
    
    
        //排序
        Arrays.sort(nums);
        List<List<Integer>> resList = new ArrayList<>();
        for (int i = 0; i < nums.length - 1; i++) {
    
    
            //标记元素必须大于0
            if (nums[0] > 0) return resList;
            //当前标记元素和之前的标记元素相等,跳过本轮循环,进行去重操作
            if (i > 0 && nums[i] == nums[i - 1]) continue;
            //标记元素,左指针,右指针
            int f = nums[i], left = i + 1, right = nums.length - 1;
            //左指针必须小于右指针
            while (left < right) {
    
    
                int s = nums[left];
                int t = nums[right];
                int total = f + s + t;
                if (total < 0) {
    
    
                    left++;
                } else if (total == 0) {
    
    
                    List<Integer> list = new ArrayList<>();
                    list.add(f);
                    list.add(s);
                    list.add(t);
                    resList.add(list);
                    //获取右边最后一个与当前元素相等的数
                    while (left < right && nums[right - 1] == nums[right]) right--;
                    //获取左边最后一个与当前元素相等的数
                    while (left < right && nums[left + 1] == nums[left]) left++;
                    //--和++ 目的是获取左右两边与当前s和t都不相等的数
                    right--;
                    left++;
                } else {
    
    
                    right--;
                }
            }
        }
        return resList;
    }

2.排序+双指针(初版,未优化,可通过)

问题:

  1. 目标数组的轮询使用了while循环。
  2. 将判重交给了Set集合判断,重复元素并没有在逻辑中去除。
 public static List<List<Integer>> threeSum(int[] nums) {
    
    
        List<List<Integer>> resList = new ArrayList<>();
        if (nums.length < 3) {
    
    
            return resList;
        }
        Arrays.sort(nums);
        int now = 0;
        while (nums[now] <= 0 && now <= nums.length - 3) {
    
    
            if (now > 0 && nums[now] == nums[now - 1]) {
    
    
                now++;
                continue;
            }
            int left = now + 1;
            int right = nums.length - 1;
            while (left < right && left < nums.length - 1 && right > 0) {
    
    
                int first = nums[now];
                int second = nums[left];
                int third = nums[right];
                int total = first + second + third;
                if (total < 0) {
    
    
                    left++;
                    continue;
                }
                if (total == 0) {
    
    
                    List<Integer> list = new ArrayList<>();
                    list.add(first);
                    list.add(second);
                    list.add(third);
                    resList.add(list);
                    left++;
                    continue;
                }
                right--;
            }
            now++;
        }
        Set<List<Integer>> sets = new HashSet<>();
        resList.forEach(item -> sets.add(item));
        List<List<Integer>> lastRes = sets.stream().collect(Collectors.toList());
        return lastRes;
    }

3. 将正数和负数分开(暴力破解,超时)

 public static List<List<Integer>> threeSum(int[] nums) {
    
    
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (nums == null || nums.length < 3) {
    
    
            return handleRes(result);
        }
        List<Integer> negatives = new ArrayList<Integer>();
        List<Integer> positive = new ArrayList<Integer>();
        int i = 0;
        for (int num : nums) {
    
    
            //0作为正数处理
            boolean a = num < 0 ? negatives.add(num) : positive.add(num);

            if (num == 0) {
    
    
                i++;
            }
        }
        if (i >= 3) {
    
    
            List<Integer> list = new ArrayList<>();
            for (int j = 0; j < 3; j++) {
    
    
                list.add(0);
            }
            result.add(list);
        }
        //无整数,直接返回
        if (positive.size() == 0) {
    
    
            return handleRes(result);
        }
        //无负数,判断0的个数
        if (negatives.size() == 0) {
    
    
            return handleRes(result);
        }
        //两个正数,一个负数
        getRes(positive, negatives, result, true);
        //两个负数,一个正数
        getRes(negatives, positive, result, false);
        return handleRes(result);
    }

    /**
     * @description: 方法一的结果返回值处理
     * @param: null
     * @return:
     * @author zhq
     * @date: 2021/10/20 20:04
     */
    public static List<List<Integer>> handleRes(List<List<Integer>> res) {
    
    
        Set<List<Integer>> list = new HashSet<>();
        res.forEach(item -> list.add(item));
        List<List<Integer>> resList = new ArrayList<>();
        list.forEach(item -> {
    
    
            resList.add(item);
        });
        return resList;
    }

    /**
     * @description: 方法一的正数或负数处理
     * @param: null
     * @return:
     * @author zhq
     * @date: 2021/10/20 20:05
     */
    public static void getRes(List<Integer> evenList, List<Integer> singularList, List<List<Integer>> result, boolean isPositive) {
    
    
        for (int i = 0; i < evenList.size() - 1; i++) {
    
    
            int first, second;
            for (int j = i + 1; j < evenList.size(); j++) {
    
    
                first = evenList.get(i);
                second = evenList.get(j);
                if (first == second && first == 0) {
    
    
                    //如果包含0,说明两个数都是整数,不符合条件
                    continue;
                }
                for (int m = 0; m < singularList.size(); m++) {
    
    
                    int third = singularList.get(m);
                    if (first + second + third == 0) {
    
    
                        List<Integer> res = new ArrayList<Integer>();
                        if (isPositive) {
    
    
                            res.add(third);
                            if (first < second) {
    
    
                                res.add(first);
                                res.add(second);
                            } else {
    
    
                                res.add(second);
                                res.add(first);
                            }
                        } else {
    
    
                            if (first < second) {
    
    
                                res.add(first);
                                res.add(second);
                            } else {
    
    
                                res.add(second);
                                res.add(first);
                            }
                            res.add(third);
                        }
                        result.add(res);
                    }
                }
            }

        }
    }
}

参考文章

排序 + 双指针,逐行解释

Guess you like

Origin blog.csdn.net/zhang19903848257/article/details/120887017