题目地址
描述
给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):0 <= a, b, c, d < n
a、b、c 和 d 互不相同
nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。示例 1:输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:输入:nums = [2,2,2,2,2], target = 8
输出:[[2,2,2,2]]提示:1 <= nums.length <= 200
-109 <= nums[i] <= 109
-109 <= target <= 109
思路
排序 + 双指针,能有效的提高遍历效率
本人题解
func fourSum(nums []int, target int) [][]int {// 排序sort.Slice(nums, func(i, j int) bool {return nums[i] < nums[j]})// 因为是四数之和,所以外边两层要通过遍历完成,内两层通过《排序 + 双指针 解两数之和》思路处理list := [][]int{}uniqueFlagMap := map[string]bool{}for i1 := 0; i1 < len(nums); i1++ {for i2 := i1 + 1; i2 < len(nums); i2++ {if l := twoSum(nums, i2+1, len(nums)-1, target-nums[i1]-nums[i2]); len(l) > 0 {for _, item := range l {// 因为不能出现重复结果,所以此处做了去重处理uniqueFlag := fmt.Sprintf("%d%d%d%d", nums[i1], nums[i2], nums[item[0]], nums[item[1]])if !uniqueFlagMap[uniqueFlag] {uniqueFlagMap[uniqueFlag] = truelist = append(list, []int{nums[i1], nums[i2], nums[item[0]], nums[item[1]]})}}}}}return list
}// 求有序数组两数之和方法
func twoSum(nums []int, i3, i4, target int) [][]int {list := [][]int{}for i3 < i4 {t := nums[i3] + nums[i4]if t == target {list = append(list, []int{i3, i4})i3++} else if t > target {i4--} else {i3++}}return list
}
在LeetCode
提交后,得分极差
优质解法
然后找了得分靠前的一位大佬的解法,果然牛逼的解法是不走寻常路的,先上代码:
func fourSum(nums []int, target int) [][]int {sort.Slice(nums, func(i, j int) bool {return nums[i] < nums[j]})n := len(nums)var ans [][]intfor a := 0; a < n-3; a++ {if a > 0 && nums[a] == nums[a-1] {continue}if nums[a] + nums[a+1] + nums[a+2] + nums[a+3] > target {break}if nums[a] + nums[n-1] + nums[n-2] + nums[n-3] < target {continue}for b := a+1; b < n-2; b++ {if b > a+1 && nums[b] == nums[b-1] {continue}c := b+1d := n-1for c < d {cur := nums[a] + nums[b] + nums[c] + nums[d]if cur == target {ans = append(ans, []int{nums[a], nums[b], nums[c], nums[d]})c += 1d -= 1for c < d && nums[c] == nums[c-1] {c += 1}for c < d && nums[d] == nums[d+1] {d -= 1}} else if cur < target {c += 1} else {d -= 1}}}}return ans
}
此代码直接借鉴大佬的,有侵权的地方,联系小编
总结
经过对优质解法的分析,得出了以下几点结论:
1、小编的整体思路是没问题的,也就是 ”排序 + 双指针“
2、优质代码增加了以下几点处理:
- 为了不做去重复处理,在遍历的过程中,就对有可能导致重复的数据做了处理。处理思路是因为数组已经做了排序,所以有可能导致重复结果的数据是挨在一起的,比如
[-1,-1,0,0,-2,-2]
,如果对第一个-1
已经做了处理,第二个-1
就不用做处理了,直接跳过即可
func fourSum(nums []int, target int) [][]int {sort.Slice(nums, func(i, j int) bool {return nums[i] < nums[j]})n := len(nums)var ans [][]intfor a := 0; a < n-3; a++ {// 跳过有可能导致重复结果的数据if a > 0 && nums[a] == nums[a-1] {continue}if nums[a] + nums[a+1] + nums[a+2] + nums[a+3] > target {break}if nums[a] + nums[n-1] + nums[n-2] + nums[n-3] < target {continue}for b := a+1; b < n-2; b++ {// 跳过有可能导致重复结果的数据if b > a+1 && nums[b] == nums[b-1] {continue}c := b+1d := n-1for c < d {cur := nums[a] + nums[b] + nums[c] + nums[d]if cur == target {ans = append(ans, []int{nums[a], nums[b], nums[c], nums[d]})c += 1d -= 1// 跳过有可能导致重复结果的数据for c < d && nums[c] == nums[c-1] {c += 1}// 跳过有可能导致重复结果的数据for c < d && nums[d] == nums[d+1] {d -= 1}} else if cur < target {c += 1} else {d -= 1}}}}return ans
}
- 对有序数组做
掐头去尾
处理
for a := 0; a < n-3; a++ {// 去尾处理if nums[a]+nums[a+1]+nums[a+2]+nums[a+3] > target {break}// 掐头处理if nums[a]+nums[n-1]+nums[n-2]+nums[n-3] < target {continue}
}
经过这两步的优化,极大程度降低了遍历的次数,同时也缩短了每次遍历的耗时,所以整体上优化了耗时