LeetCode.15-三数之和(Swift)

155 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情 。如果哪里写的不对,请大家评论批评。

希望往后的日子,可以每天坚持一个算法,最近发现一个有意思的事情,LeetCode中等难度的题,也不简单,暴力算法固然能解决问题,但是从时间复杂度和空间复杂度上肯定达不到要求。

三数之和

题目

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

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

分析

  1. 如果只是需要判定三数之和,这其实和两数之和没什么区别
  2. 提出中注意的点才是重重之中。
  3. 假设我们使用暴力解法(三重循环)固然可以得到三数相加为0的数,但是去除重复的的数据又要不好的逻辑。
  4. 怎么做呢????
  5. 先排序好不好?这样计算的时候可以去重。OK~ 我们先排序,从小到大
  6. 如果第一位最小的值大于0,是不是就不可能实现三数的和为0!答案肯定的1+1+1>0也大于。如果最后一位都小于0,那整个数组都不成立相加大于0
  7. 当然数组不够三位数,也没办法计算。
if count < 3 { return [] }
let list = nums.sorted()
if list[0] > 0 { return [] }

下面才进入算法的精髓,看图解惑吧

图解

双指针

  • 原始值以及排序之后三数之和.drawio (1).png
  1. 分析中已经说明,进入到后面算法的数组,肯定包含正数和负数。
  2. 双指针法的核心其实是把0位&1位&last位相加是否等于0
  3. 如果大于0说明正数太大了,我们可以进行last-1在进行比较,如果还是大于0,继续last-1,直到2位。
  4. 如果小于0说明负数缺少正数,需要向右移动1位,进行1位+1,依次比较,同上
  5. 如果正好等于0,可以把当前的值保存到数组了。然后1位,进行1位+1last位,进行last-1

例子

  1. 进行第一次循环,i位不变,j、k去做对比,发现小于0j + 1,一直到j < k结束当前循环 三数之和.drawio (4).png

  2. 第二次循环,i+1,j=i+1,k=count-1,这一次找到了两组三数之和.drawio (3).png

  3. 然后继续三数之和.drawio (5).png

  4. 最后一步三数之和.drawio (6).png

注意:: 说好的去重呢?

稍等!稍等! 先看例子

假设

三数之和.drawio (8).png

  1. 如果不去重,会得到[-1,-1,2],[-1,-1,2]两组重复的数据下标分别是[1,2,6],[3,4,6]`
  2. 怎么处理呢?
  3. 回到双指针的第5步,这之后j+1,k-1,在这之前是不是可以先判断一下j位置的值是不是等于j+1位置的值,进行while循环一直到后面的值和j位置的数不一致。***不要超过K的位置 ***
  4. 当然K得位置也一样判断,只不过这边是k-1循环下去。
  5. OK,下面看代码吧。

代码

class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        var res:[[Int]] = []
        let count = nums.count
        // 如果小于3,没必要比较,条件不成立,直接返回nil数组
        if count < 3 {
            return res
        }
        // 先排序
        let list = nums.sorted()
        // 升序,第一位就大于0,或者最后一位小于0,不可能会出现相加为0,直接返回nil数组
        if list[0] > 0 || list[count - 1] < 0{            
            return res
        }
        // 记录双指针
        var j = 0
        var k = 0
        
        // 因为有双指针,没必须循环到最后一位,可以少循环一位,为什么不能减少两位呢?因为正好3位,0..<0,不会进行循环
        for i in 0..<list.count - 2 {
            // 这里是为了给i进行去重,如果已经计算过,就不需要再次计算了
            if i > 0 && list[i] == list[i-1] {
                continue
            }
            // 两个指针赋值
            j = i + 1
            k = count - 1
            // 开始循环,左指针小于右指针
            while j < k {
                // 如果三者相加等于0
                if list[i] + list[j] + list[k] == 0 {
                    // 条件满足加入数组
                    res.append([list[i], list[j], list[k]])
                    // 左指针去重
                    while j < k && list[j]==list[j+1] {
                        j = j + 1
                    }
                    // 右指针去重
                    while j < k && list[k]==list[k - 1] {
                        k = k - 1
                    }
                    // 左右指针移位
                    j = j + 1
                    k = k - 1
                }else if list[i] + list[j] + list[k] > 0 {// 如果大于0,说明右指针值太大了,单方面减小
                    k = k - 1
                }else{// 如果小于0,说明左指针值太小了,单方面增大
                    j = j + 1
                }
            }
        }
        return res
    }
}
class Solution {
    func threeSum(_ nums: [Int]) -> [[Int]] {
        if nums.count < 3 {
            return []
        }
        let nums = nums.sorted()
        var reslutArray = [[Int]]()
        for i in 0..<nums.count-2 {
            if i>0&&nums[i] == nums[i-1] {
                continue
            }
            insertArray(nums, i, to: &reslutArray)
        }
        return reslutArray
    }

    //! 插入三元数组
    func insertArray(_ nums:[Int],_ current:Int, to resultArr: inout  [[Int]]) {
        var left = current+1
        var right = nums.count-1
        while left<right {
            let sum = nums[current]+nums[left]+nums[right]
            if sum < 0 {
                left += 1
            } else if sum > 0 {
                right -= 1
            } else {
                resultArr.append([nums[current],nums[left],nums[right]])
                while left < right && nums[left] == nums[left+1] {
                    left += 1
                }
                while left < right && nums[right] == nums[right-1] {
                    right -= 1
                }
                left+=1
                right-=1
            }
        }
    }
}