15. 三数之和 + 双指针

题目来源

LeetCode-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

相似题目

15. 三数之和
1. 两数之和
18.四数之和

题目解析

解法一:双指针

java代码

package com.walegarrett.interview;

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

/**
 * @Author WaleGarrett
 * @Date 2021/2/21 20:21
 */

/**
 * 题目描述:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?
 * 请你找出所有和为 0 且不重复的三元组。
 */
public class LeetCode_15 {
    public List<List<Integer>> threeSum(int[] nums) {
        int len = nums.length;
        List<List<Integer>> result = new ArrayList<>();
        Arrays.sort(nums);
        //为防止重复,从小到大枚举a
        for(int a=0; a<len; a++){
            //需要和上次枚举的数不同
            if(a>0 && nums[a] == nums[a-1])
                continue;
            //c对应从最右端开始枚举
            int c = len-1;
            int leave = -nums[a];
            for(int b=a+1; b<len; b++){
                if(b > a+1 && nums[b] == nums[b-1])
                    continue;
                // 需要保证 b 的指针在 c 的指针的左侧
                while(b<c && nums[b]+nums[c] > leave){
                    c--;
                }
                if(b == c)
                    break;
                if(nums[b] + nums[c] == leave){
                    List<Integer> list = new ArrayList<>();
                    list.add(nums[a]);list.add(nums[b]);list.add(nums[c]);
                    result.add(list);
                }
            }
        }
        return result;
    }
}

复杂度分析

  1. 时间复杂度:O(\(N^2\)),其中 N 是数组 nums 的长度。
  2. 空间复杂度:O(logN)。我们忽略存储答案的空间,额外的排序的空间复杂度为 O(logN)。然而我们修改了输入的数组 nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了 nums 的副本并进行排序,空间复杂度为 O(N)。

参考

LeetCode官方题解

解法二:双指针-nSum问题模板

代码实现

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        int n = nums.length;
        Arrays.sort(nums);
        return nSum(nums, 3, 0, 0);
    }

    private List<List<Integer>> nSum(int[] sum ,int n, int pos, int target){
        int size = sum.length;
        List<List<Integer>> res = new ArrayList<>();
        if(n < 2 || size < n){
            return res;
        }else if(n == 2){
            int left = pos, right = size-1;
            while(left < right){
                int tempsum = sum[left] + sum[right];
                int leftnum = sum[left], rightnum = sum[right];
                if(tempsum < target){
                    while(left < right && sum[left] == leftnum){
                        left++;
                    }
                }else if(tempsum > target){
                    while(left < right && sum[right] == rightnum){
                        right--;
                    }
                }else{
                    List<Integer> list = new ArrayList<>(){{
                        add(leftnum);
                        add(rightnum);
                    }};
                    res.add(list);
                    while(left < right && sum[left] == leftnum){
                        left++;
                    }
                    while(left < right && sum[right] == rightnum){
                        right--;
                    }
                }
            }
        }else{
            for(int i=pos; i<size; i++){
                int num = sum[i];
                List<List<Integer>> ans = nSum(sum, n-1, i+1, target-num);
                for(List<Integer> temp : ans){
                    temp.add(num);
                    res.add(temp);
                }
                while(i < size - 1 && sum[i] == sum[i+1]){
                    i++;
                }
            }
        }
        return res;
    }
}

参考

一个方法团灭 nSum 问题

posted @ 2021-02-22 22:29  Garrett_Wale  阅读(72)  评论(0编辑  收藏  举报