思路:
- 我们根据题目意思,可以移除nums里面的部分数字,规则如下:0的数量最多为3,其他数字的数量最多为2
- 我们创建两个map,分别记作map1和map2 其中 map1的k:nums[i], v:i; map2 为 k : nums[i] + nums[j], v 为 []int{i,j},在添加进入map2的时候,我们应该判断nums[i]和nums[j] 这两个值是否在map2[nums[i] + nums[j]]这个slice里面,并且这个slice我们每两个划分为一个i j对
- 上面的操作进行完成之后,我们就可以遍历map1 得到 k,v,然后我们判定 map2[-k]是否存在,是的话,就是满足我们要求的,然后取出,最后就是去重,通过map来去重即可,去重时间复杂度为 O(n )
说明
循环暴力时间复杂度过不了
复杂度分析
t: O(n^2)
s: O(n)
题目链接
https://leetcode.cn/problems/3sum/
代码1
func threeSum(nums []int) [][]int {
sort.Ints(nums)
nums = removeSameTwo(nums)
var numsSlice [][3]int
// 键表示的是 值,value 表示的是 index
var numMap = map[int]int{}
// 键表示的是 两个数字相加的值, value 表示的是两个数字的下标组成的数组(注意这点不是slice)
var num2Map = map[int][]int{}
for i, num := range nums {
numMap[num] = i
for j := i + 1; j < len(nums); j++ {
if _, ok := num2Map[num+nums[j]]; ok {
num2Map[num+nums[j]] = append(num2Map[num+nums[j]], i, j)
} else {
num2Map[num+nums[j]] = []int{i, j}
}
}
}
for k, v := range num2Map {
if v2, ok := numMap[-k]; ok {
for i := 0; i < len(v)/2; i++ {
if isDifThree(v2, v[i*2], v[i*2+1]) {
numsSlice = append(numsSlice, getSequenceArr([...]int{nums[v2], nums[v[i*2]], nums[v[i*2+1]]}))
}
}
}
}
var diffCount int
var diffMap = map[[3]int]struct{}{}
for _, numArr := range numsSlice {
if _, ok := diffMap[numArr]; ok {
continue
} else {
numsSlice[diffCount] = numArr
diffMap[numArr] = struct{}{}
diffCount++
}
}
var rNumsArr = make([][]int, diffCount)
for i, nums2 := range numsSlice[:diffCount] {
rNumsArr[i] = []int{nums2[0], nums2[1], nums2[2]}
}
return rNumsArr
}
func getSequenceArr(nums [3]int) [3]int {
var temp = nums[:]
sort.Ints(temp)
return [3]int{temp[0], temp[1], temp[2]}
}
func isDifThree(a, b, c int) bool {
if a != b && b != c && a != c {
return true
}
return false
}
// 去掉多余的数字
func removeSameTwo(nums []int) []int {
fmt.Println(nums)
var count, val, valC int
val = nums[0]
valC = 1
for i := 1; i < len(nums); i++ {
if val == nums[i] && i != len(nums)-1 {
valC++
} else {
if val == 0 {
if valC >= 3 {
nums[count] = 0
nums[count+1] = 0
nums[count+2] = 0
count += 3
} else if valC == 2 {
nums[count] = val
nums[count+1] = val
count += 2
} else {
nums[count] = val
count += 1
}
} else {
if valC >= 2 {
nums[count] = val
nums[count+1] = val
count += 2
} else {
nums[count] = val
count += 1
}
}
valC = 1
val = nums[i]
if i == len(nums)-1 {
nums[count] = nums[len(nums)-1]
count++
}
}
}
fmt.Println(nums[:count])
return nums[:count]
}
代码2
代码一在遍历的过程中没有达到真正去重的效果,但是代码2的话,通过排序之后可以直接取到一个不同的数字,代码1里面明显使用map来存储比较耗费时间,并且代码1最后通过map实现去重不是很有必要,现在的话,在加入数字的过程中就避免了重复
func threeSum(nums []int) [][]int {
sort.Ints(nums)
if nums[0] == 0 && nums[len(nums)-1] == 0 && len(nums) > 2 {
return [][]int{{0, 0, 0}}
}
var rNumsArr = make([][]int, 0)
// 不是必须的
nums = removeSameTwo(nums)
fmt.Println(nums)
var numsMap = map[int]int{}
var zeroIndexBefore, zeroIndexAfter = -1, -1
for index, value := range nums {
// 这样我们就是下标为相同数字的最后面的一个
numsMap[value] = index
if index+1 < len(nums) && nums[index+1] >= 0 && value < 0 && zeroIndexBefore == -1 {
zeroIndexBefore = index
} else if index > 0 && nums[index-1] <= 0 && value > 0 {
zeroIndexAfter = index
}
}
// 前两个为负数
for i := 0; i <= zeroIndexBefore; i++ {
// 表示的是取到的数字不同并且下标>0
if i == 0 || nums[i] != nums[i-1] {
for j := i + 1; j <= zeroIndexBefore; j++ {
// 表示的是取到的数字不同并且下标>0
if nums[j] != nums[j-1] || j == i+1 {
if _, ok := numsMap[-(nums[i] + nums[j])]; ok && numsMap[-(nums[i]+nums[j])] >= zeroIndexAfter {
rNumsArr = append(rNumsArr, []int{nums[i], nums[j], -(nums[i] + nums[j])})
}
}
}
}
}
// 仅仅第一个为负数
for i := 0; i <= zeroIndexBefore; i++ {
// 表示的是取到的数字不同并且下标>0
if i == 0 || nums[i] != nums[i-1] {
for j := zeroIndexBefore + 1; j < len(nums); j++ {
// 表示的是取到的数字不同并且下标>0
if nums[j] != nums[j-1] || j == zeroIndexBefore {
if _, ok := numsMap[-(nums[i] + nums[j])]; ok && numsMap[-(nums[i]+nums[j])] >= zeroIndexAfter && j < numsMap[-(nums[i]+nums[j])] {
rNumsArr = append(rNumsArr, []int{nums[i], nums[j], -(nums[i] + nums[j])})
}
}
}
}
}
// 没有负数
if zeroIndexAfter-zeroIndexBefore >= 4 {
rNumsArr = append(rNumsArr, []int{0, 0, 0})
}
return rNumsArr
}
func removeSameTwo(nums []int) []int {
var count, val, valC int
val = nums[0]
valC = 1
for i := 1; i < len(nums); i++ {
if val == nums[i] && i != len(nums)-1 {
valC++
} else {
if val == 0 {
if valC >= 3 {
nums[count] = 0
nums[count+1] = 0
nums[count+2] = 0
count += 3
} else if valC == 2 {
nums[count] = val
nums[count+1] = val
count += 2
} else {
nums[count] = val
count += 1
}
} else {
if valC >= 2 {
nums[count] = val
nums[count+1] = val
count += 2
} else {
nums[count] = val
count += 1
}
}
valC = 1
val = nums[i]
if i == len(nums)-1 {
nums[count] = nums[len(nums)-1]
count++
}
}
}
return nums[:count]
}
总结
虽然使用map查找方便很多,但是map底层hash算法也需要时间的