给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,cd ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。

    1. //调用三数和的API
    2. func fourSum(nums []int, target int) [][]int {
    3. sort.Sort(sort.IntSlice(nums)) // 从小到大排序
    4. ret := make([][]int,0)
    5. for i := 0; i < len(nums)-3; i++ {
    6. // if nums[i] > target { break }
    7. if i > 0 && nums[i] == nums[i-1] { continue } // i去重
    8. threeRet := threeSum(nums[i+1:len(nums)], target-nums[i])
    9. if len(threeRet) == 0 { continue }
    10. for _, value := range threeRet {
    11. tmp := make([]int,3)
    12. copy(tmp,value)
    13. tmp = append(tmp, nums[i])
    14. ret = append(ret, tmp)
    15. }
    16. }
    17. return ret
    18. }
    19. func threeSum(nums []int, target int) [][]int {
    20. if len(nums) < 3 { return [][]int{} }
    21. ret := make([][]int, 0, 0)
    22. // 从最左侧开始固定,l r为固定i后开始对右侧部分遍历
    23. for i := 0; i < len(nums) - 1; i++ {
    24. l, r := i + 1, len(nums) - 1
    25. // if nums[i] > target || nums[i] + nums[l] > target { break } // 优化掉不可能的情况
    26. if i > 0 && nums[i] == nums[i-1] { continue } // i 去重 如果nums[i-1]时已考虑了nums[i]的全部情况
    27. for l < r {
    28. if l > i + 1 && nums[l] == nums[l-1] { // l 去重
    29. l++; continue
    30. }
    31. if r < len(nums) - 2 && nums[r] == nums[r+1] { // r 去重
    32. r--; continue
    33. }
    34. if nums[i] + nums[l] + nums[r] > target { //
    35. r--
    36. } else if nums[i] + nums[l] + nums[r] < target {
    37. l++
    38. } else {
    39. ret = append(ret, []int{nums[i], nums[l], nums[r]})
    40. l++; r--
    41. }
    42. }
    43. }
    44. return ret
    45. }
    //滑动窗口 + 二分法查找指定数值
    func fourSum(nums []int, target int) [][]int {
        i := 0  // 左指针
        j := 0  // 右指针
        mid := 0 // 中间值指针
        midLeft := 0 
        midRight := 0
        numsLen := len(nums)
        result := make([][]int,0,0)
        if numsLen < 4 {    // 元素数不符合
            return result
        }
        sortNums := sort.IntSlice(nums)
        sortNums.Sort()
        for k := 0; k < numsLen - 3 /* 如果后面不够4个元素 提前结束*/; k++ {
            if k >0 && nums[k] == nums[k - 1] { // 当前元素与上个元素重复,不用在查找, 因为查到的也是一样的数据
                continue
            }
            if nums[k] + nums[k+1] + nums[k+2] + nums[k+3] > target { // 最小四个值之和 大于目标值
                return result
            }
            mid = 0
            midLeft  = 0
            midRight = 0
            for i = k+1; i < numsLen - 2 /*保证i后面至少还需要2个元素*/; i++ {
                if i > k + 1 && nums[i] == nums[i - 1] { // 当前元素与上个元素重复,不用在查找, 因为查到的也是一样的数据
                    continue
                }
                j = numsLen - 1
                for i < j-1 {
                    if nums[k] + nums[i] + nums[j] + nums[j-1] < target  {
                        break
                    }
                    if nums[k] + nums[i] + nums[j] >= target && nums[i] > 0  {
                        j--
                        continue
                    }
                    needNumber := target - ( nums[k] + nums[i] + nums[j] )
                    // nums[k] + nums[i] + nums[j] < target
                    // 在 (i,j)内找到一个符合 nums[k] + nums[i] + nums[j] + nums[mid] == target即可
                    midLeft = i+1
                    midRight = j-1
                    // 二分查找 查到符合的值
                    for midLeft <= midRight {
                        mid = midLeft + ( (midRight - midLeft) >> 1)
                        if nums[mid] > needNumber {
                            midRight = mid - 1
                            continue
                        }
                        if nums[mid] < needNumber {
                            midLeft = mid + 1
                            continue
                        }
    
                        if nums[mid] == needNumber {
                            // 跳过重复的
                            lastIndex := len(result) - 1
                            if lastIndex >= 0 && result[lastIndex][0] == nums[k] &&
                                result[lastIndex][1] == nums[i] &&
                                result[lastIndex][2] == nums[mid] &&
                                result[lastIndex][3] == nums[j]{
                                break
                            }
                            result = append(result, []int{
                                nums[k],
                                nums[i],
                                nums[mid],
                                nums[j],
                            })
                            break
                        }
    
                     }
                    j--
                }
            }
        }
        return result
    }
    
    //三数之和➕一层固定判断
    func fourSum(nums []int, target int) [][]int {
        // 对数组进行排序
        Sort(nums)
        n := len(nums)
        // 定义返回的数组
        result := make([][]int, 0)
        // 从数组左侧开始进行遍历,固定左侧第一个值
        for i := 0; i < n - 3; i++ {
            // 如果最小值都大于0,则整个数组都没有符合要求的组合
            if nums[i] + nums[i+1] + nums[i+2] + nums[3] > target {
                break
            }
            // 确保nums[i]改变了
            if i > 0 && nums[i] == nums[i-1] {
                continue
            }
            // 固定第二个值,第二个值都取值范围为外循环左侧+1到外循环右侧-1
            // 这里其实可以理解为对三数求和了,步骤与三数求和一致了
            for b := i + 1; b < (n - 2); b++ {
                // 去重
                if b > i + 1 && nums[b] == nums[b-1] {
                    continue
                }
                //左右两侧都固定好了,用左指针和右指针夹逼
                L := b + 1
                R := n - 1
                for L < R {
                    sum := nums[i] + nums[b] + nums[L] + nums[R]
                    if sum == target {
                        // 保存四个数
                        item := make([]int, 4)
                        item[0] = nums[i]
                        item[1] = nums[b]
                        item[2] = nums[L]
                        item[3] = nums[R]
                        result = append(result, item)
                        // 左指针去重
                        for L < R && nums[L] == nums[L+1] {
                            L++
                        }
                        // 右指针去重
                        for L < R && nums[R] == nums[R-1]{
                            R--
                        }
    
                        R--
                        L++
    
                    } else if sum > target {
                        // 和比目标值大,因为左侧如果继续右移,值只会比当前值大,所以需要右指针变小//左移动
                        R--
                    } else {
                        L++
                    }
                }
            }
        }
        return result
    }
    
    func Sort(nums []int) {
        sort(nums, 0, len(nums) - 1)
    }
    
    func sort(nums []int, start int, end int) {
        if start >= end {
            return
        }
        i := cut(nums, start, end)
        sort(nums, start, i - 1)
        sort(nums, i + 1, end)
    }
    
    func cut(arr []int, start int, end int) int {
        pivot := arr[end]
        p := start
        q := start
        for ; q < end; q++ {
            if arr[q] < pivot {
                arr[p], arr[q] = arr[q], arr[p]
                p++
            }
        }
        arr[p], arr[end] = arr[end], arr[p]
        return p
    }