思路:

  1. 我们根据题目意思,可以移除nums里面的部分数字,规则如下:0的数量最多为3,其他数字的数量最多为2
  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对
  3. 上面的操作进行完成之后,我们就可以遍历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算法也需要时间的