三数之和(排序+双指针)

326 阅读1分钟

三数之和


描述

给你一个包含 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

思路:排序+双指针
  • 首先排序数组 nums,使得取得的三数 (a, b, c) 满足 a≤b≤c ,这样后续计算就不会出现 (b, a, c)、(c, b, a) 的组合,达到去重的效果。

  • 0..<(len - 2) 范围内枚举第一个数 a,并在之后的区间找出所有满足 a+b+c=0bc

    • 如果 a > 0,那么 bc 也大于0,则三数之和必不为 0,则结束循环。

    • 如果 a 和 前一个数重复,此数已经选取过,则跳过本次循环。

    • 定义左右指针 leftright 于 a 后续区间的两端(left 为 b,right 为c),根据 sum = a + left + right 的结果移动指针,找出满足三数之和为 0 的结果(指针移动要注意去重,即循环移动指针直到 新值 != 旧值

      • 如果 sum = 0,则记录结果,同时收缩左右指针
      • 如果 sum > 0 ,左移右指针
      • 如果 sum < 0 ,右移左指针
复杂度分析
  • 时间复杂度:O(n2) ,数组排序复杂度为 O(nlogn) ,枚举 a 的时间复杂度为 O(n) ,双指针滑动查找 bc 的时间复杂度为O(n) ,所以总的时间复杂度为 O(nlogn)+O(n2) = O(n2)
  • 空间复杂度:O(n)|O(logn) ,空间复杂度取决于排序使用的空间复杂度。
题解(swift)
 class Solution {
     func threeSum(_ nums: [Int]) -> [[Int]] {
         var res = [[Int]]()
         let len = nums.count
         // 排序数组
         let nums = nums.sorted()
         // 不满足三数
         if len < 3 {
             return res
         }
         // 枚举数 a,使用双指针遍历 a 之后的区间,找到满足 a+b+c=0 的所有不重复值
         for fir in 0..<(len - 2) {
             // 第一个元素大于0不存在三数和为0,直接结束
             if nums[fir] > 0 {
                 break
             }
             // 第一个元素去重
             if fir > 0 && nums[fir] == nums[fir - 1] {
                 continue
             }
             // 定义左右指针在 a 后区间的两端,根据情况收缩指针
             var left = fir + 1,right = len - 1
             while right > left {
                 let sum = nums[fir] + nums[left] + nums[right]
                 if sum == 0 {
                     //三数和为0,添加到数组,同时收缩两端指针
                     res.append([nums[fir], nums[left], nums[right]])
                     repeat {
                         left += 1
                     } while left < right && nums[left] == nums[left - 1]
                     repeat {
                         right -= 1
                     } while left < right && nums[right] == nums[right + 1]
                 } else if sum > 0 {
                     // sum 太大,左移右指针
                     right -= 1
                 } else {
                     // sum 太大,右移左指针
                     left += 1
                 }
             }
         }
         return res
     }
 }
实战练习

[leedcode:15.三数之和]