给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
输入:nums = [1,0,-1,0,-2,2], target = 0 输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/4s… 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路分析
类似三数之和问题,按照以下思路解决:
- 从小到大排序
- 两重循环枚举前两个数组,另外两个数字通过双指针来查找
实现代码
代码涉及到Go的知识包括:
-
数组的使用
ans := make([][]int, 0) -
append函数的使用
ans = append(ans, []int{nums[i], nums[j], nums[left], nums[right]}) -
Go中的while循环:
for condition { // loop }实现完整代码如下:
/**
* 实现思路:
1. 从小到大排序
2. 两重循环枚举前两个数字,另外两个数字使用双指针来查找:一个指针指向开头,一个指针指向尾部
*/
func fourSum(nums []int, target int) [][]int {
l := len(nums)
if l <= 3 {
return [][]int{}
}
sort.Ints(nums)
// fmt.Println(nums)
ans := make([][]int, 0)
for i := 0; i < l-3; i++ {
// fmt.Println("i = ", i)
// if nums[i] > target {
// break
// }
for j := i + 1; j < l-2; j++ {
// fmt.Println("j = ", j)
// if nums[i]+nums[j] > target {
// break // 此时外边的循环也应该跳出了,怎么免除这些操作?
// }
for left, right := j+1, l-1; left < right; {
// fmt.Println("i = ", i, ", j = ", j, ", left = ", left, ", right = ", right)
// fmt.Println("left = ", left, ", right = ", right)
sum := nums[i] + nums[j] + nums[left] + nums[right]
// fmt.Println("i = ", i, ", j = ", j, ", left = ", left, ", right = ", right, ", sum = ", sum)
if sum > target {
// fmt.Println("和 大于 target, right左移")
right--
continue
}
if sum < target {
// fmt.Println("和 小于 target, left右移")
left++
continue
// sum = nums[i] + nums[j] + nums[left] + nums[right]
}
if sum == target {
// fmt.Println([]int{nums[i], nums[j], nums[left], nums[right]})
ans = append(ans, []int{nums[i], nums[j], nums[left], nums[right]})
// 忽略下一个相同的数字
for nums[left+1] == nums[left] && left+1 < right {
// fmt.Println("判断left右侧数字是否相同")
left++
}
left++
continue
}
// 梳理逻辑后,发现此部分代码永远不会执行:忽略下一个相同的数字
// for nums[left+1] == nums[left] && left+1 < right {
// fmt.Println("判断left右侧数字是否相同")
// left++
//}
//for nums[right-1] == nums[right] && right-1 > left {
// fmt.Println("判断right左侧数字是否相同")
// right--
//}
}
for j+1 < l && nums[j+1] == nums[j] {
j++
}
}
for i+1 < l && nums[i+1] == nums[i] {
i++
}
}
return ans
}
总结
经过与官方代码对比后,发现有以下优化的点:
- 排序后,第一层循环中nums[ i ],如何判断数组后续是否可能存在其他三个数字与其之和为target?对于第二层循环中的nums[ j ]又该如何判断呢?
↑答↑:若nums[ i ]与数组最大的三个数,即数组最后的三个数,之和小于target,则该数组中不可能存在其他三个数字满足条件。同理,对于nums[ j ] 判断其与num[ i ] 以及数组最大的两个数,即数组最大两个数,之和小于target,则数组中不可能存在其他两个数字满足条件。
if i > 0 && nums[i] == nums[i-1] || nums[i]+nums[n-3]+nums[n-2]+nums[n-1] < target {
continue
}
- 对于双指针部分,在查找到一个数字之后,如何快速移动双指针?
↑答↑:因为数组是有序的,因此在查找到一个解后,两个指针需要同时移动到下一个不等的值进行查找。
for left++; left < right && nums[left] == nums[left-1]; left++ {
}
for right--; left < right && nums[right] == nums[right+1]; right-- {
}