携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第12天,点击查看活动详情 。如果哪里写的不对,请大家评论批评。
希望往后的日子,可以每天坚持一个算法,最近发现一个有意思的事情,LeetCode
中等难度的题,也不简单,暴力算法固然能解决问题,但是从时间复杂度和空间复杂度上肯定达不到要求。
三数之和
题目
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意: 答案中不可以包含重复的三元组。
分析
- 如果只是需要判定三数之和,这其实和两数之和没什么区别
- 提出中注意的点才是重重之中。
- 假设我们使用暴力解法(三重循环)固然可以得到三数相加为0的数,但是去除重复的的数据又要不好的逻辑。
- 怎么做呢????
- 先排序好不好?这样计算的时候可以去重。OK~ 我们先排序,从小到大
- 如果
第一位最小的值大于0
,是不是就不可能实现三数的和为0!答案肯定的1+1+1>0也大于。如果最后一位都小于0
,那整个数组都不成立相加大于0
。 - 当然数组不够三位数,也没办法计算。
if count < 3 { return [] }
let list = nums.sorted()
if list[0] > 0 { return [] }
下面才进入算法的精髓,看图解惑吧
图解
双指针
- 原始值以及排序之后
- 在
分析
中已经说明,进入到后面算法的数组,肯定包含正数和负数。 - 双指针法的核心其实是把
0
位&1
位&last
位相加是否等于0
- 如果
大于0
说明正数太大了,我们可以进行last-1
在进行比较,如果还是大于0
,继续last-1
,直到2
位。 - 如果
小于0
说明负数缺少正数,需要向右移动1
位,进行1位+1
,依次比较,同上 - 如果正好
等于0
,可以把当前的值保存到数组了。然后1
位,进行1位+1
,last
位,进行last-1
例子
-
进行第一次循环,i位不变,j、k去做对比,发现
小于0
,j + 1
,一直到j < k
结束当前循环 -
第二次循环,
i+1
,j=i+1
,k=count-1
,这一次找到了两组 -
然后继续
-
最后一步
注意:: 说好的去重呢?
稍等!稍等! 先看例子
假设
- 如果不去重,会得到
[-1,-1,2],[-1,-1,2]
两组重复的数据下标分别是[1,2,6],[3,4,6]` - 怎么处理呢?
- 回到双指针的第5步,这之后
j+1,k-1
,在这之前是不是可以先判断一下j
位置的值是不是等于j+1
位置的值,进行while循环一直到后面的值和j
位置的数不一致。***不要超过K的位置 *** - 当然K得位置也一样判断,只不过这边是
k-1
循环下去。 - 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
}
}
}
}