思路:
- 我们根据题目意思,可以移除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算法也需要时间的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步