题目
思路
说明:排序+双指针法
先排序,然后在当前元素的右侧的最左侧、最右侧设为左、右指针
注意,当前值固定时,左右指针遇到符合条件的元素后,仍然可能有其他符合条件的元素,但是需要跳过重复的元素,跳过重复的元素,只需要让左右指针都移动
错误思路:
判断是否有重复的,三个值相同,其实只要两个值相同就算重复,然后又是有顺序的,最小的两个值相同即可,用map存,map 使用前需要初始化
这种麻烦
//var myMap map[int][]int
//myMap使用前需要初始化
myMap := make(map[int][]int)
if !contains(myMap[i], left) {
result = append(result, []int{nums[i], nums[left], nums[right]})
myMap[i] = append(myMap[i], nums[left])
}
func contains(slice []int, val int) bool {
for _, num := range slice {
if num == val {
return true
}
}
return false
}
代码
func threeSum(nums []int) [][]int {
if len(nums) == 0 {
return [][]int{}
}
//先排序,再使用双指针
sort.Ints(nums)
var result [][]int
i := 0
//i右侧一定有两个元素
for i < len(nums)-2 {
left, right := i+1, len(nums)-1
val := 0 - nums[i]
for left < right {
if nums[left]+nums[right] < val {
left++
} else if nums[left]+nums[right] > val {
right--
} else {
result = append(result, []int{nums[i], nums[left], nums[right]})
//不应该break,因为可能还能继续找的到,随便left或right变下,但是为了避免重复,left应该变到下个不相同的元素
//break
//这里左右指针都移动到不重复的元素
curLeft := nums[left]
left++
for left < right && nums[left] == curLeft {
left++
}
curRight := nums[right]
right--
for left < right && nums[right] == curRight {
right--
}
}
}
//找到下个不重复的i
curVal := nums[i]
i++
for i < len(nums)-2 && nums[i] == curVal {
i++
}
}
return result
}
变体1 最接近的三数之和
这个只返回最接近的值,且只有一个,更简单。也是排序+双指针
func threeSumClosest(nums []int, target int) int {
//先排序,再使用双指针
sort.Ints(nums)
i := 0
result := nums[0] + nums[1] + nums[len(nums)-1]
//与目标值的插值
cha := math.Abs(float64(result - target))
//i右侧一定有两个元素
for i < len(nums)-2 {
left, right := i+1, len(nums)-1
for left < right {
newVal := nums[i] + nums[left] + nums[right]
tmpCha := math.Abs(float64(newVal - target))
if tmpCha < cha {
result = newVal
cha = tmpCha
}
if newVal < target {
left++
} else if newVal > target {
right--
} else {
return newVal
}
}
i++
}
return result
}
变体 2 四数之和
也是排序+双指针,没什么区别
固定 i j,右侧 left right 双指针,i j 的变化按照两层循环递增的方式
func fourSum(nums []int, target int) [][]int {
if len(nums) < 4 {
return [][]int{}
}
//先排序,再使用双指针
sort.Ints(nums)
var result [][]int
i := 0
for i < len(nums)-3 {
j := i + 1
for j < len(nums)-2 {
left, right := j+1, len(nums)-1
val := target - nums[j] - nums[i]
for left < right {
if nums[left]+nums[right] < val {
left++
} else if nums[left]+nums[right] > val {
right--
} else {
result = append(result, []int{nums[i], nums[j], nums[left], nums[right]})
//不应该break,因为可能还能继续找的到,随便left或right变下,但是为了避免重复,left应该变到下个不相同的元素
//break
//这里左右指针都移动到不重复的元素
curLeft := nums[left]
left++
for left < right && nums[left] == curLeft {
left++
}
curRight := nums[right]
right--
for left < right && nums[right] == curRight {
right--
}
}
}
//找到下个不重复的i
curVal := nums[j]
j++
for j < len(nums)-2 && nums[j] == curVal {
j++
}
}
curVal := nums[i]
i++
for i < len(nums)-3 && nums[i] == curVal {
i++
}
}
//i右侧一定有两个元素
return result
}