算法篇:二分法之k个数之和

时间:2022-07-24
本文章向大家介绍算法篇:二分法之k个数之和,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

算法:

这类题目是二分法的典型题目,核心点就是实现二数之和,三数之和,四数之和,多数之和都属于这类题目的变形题目。本文章统一整理了一个套路,多数之后最终转化为二数之和,通过递归来获取结果。基本步骤如下:

1.先将无序的数组排序。
2.按照典型的二数之和算法求解两数之和。
3.将多数之和,采用递归的方式最终用两数之和计算结果。

题目1: 两数之和

https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/

代码实现:

func twoSum(numbers []int, target int) []int {
    l := len(numbers)
    if  l == 0 {
        return nil
    }
    start, end:= 0, l-1
    var tar []int
    for start < end {
        sum := numbers[start] + numbers[end]
        if  sum == target {
            tar = append(tar,start+1)
            tar = append(tar,end+1)
            return tar
        } else if sum > target {
            end --
        } else {
            start++
        }
    }
    return tar
}

执行结果:

题目2:三数之和

https://leetcode-cn.com/problems/3sum/

代码实现:

func threeSum(nums []int) [][]int {
    sort.Ints(nums)
    res := [][]int{}
    for i:=0;i<len(nums);i++{
        arr := nums[i+1:]
        r := twoSum(arr,0-nums[i])
        if r!=nil {
            for _,v:=range r {
                tmp := []int{nums[i]}
                tmp = append(tmp,v...)
                res = append(res,tmp)
            }
        }
        for i+1 < len(nums)-1 && nums[i]==nums[i+1] {
            i++
        }
    }
    return res
}
func twoSum(nums []int, sum int) (res [][]int) {
    i,j:=0,len(nums)-1
    for  i<j {
        if nums[i]+nums[j]<sum {
            i++
        } else if nums[i]+nums[j]>sum {
            j--
        } else {
            tmp := []int{nums[i], nums[j]}
            res = append(res, tmp) 
            for i+1<j && nums[i+1] == nums[i] {
                i++
            }
            for j-1>i && nums[j-1] == nums[j] {
                j--
            }
            i++
            j--
        }
    }
    return 
}

执行结果:

题目3:四数之和

https://leetcode-cn.com/problems/4sum/

代码实现:

func fourSum(nums []int, target int) [][]int {
    var res [][]int 
    if len(nums) < 4 {
        return res
    }
    sort.Ints(nums) // 排序
    res = findNSum(nums, target, 4)
    return res
}

func findNSum(nums []int, target int, n int) [][]int {
    var res [][]int
    // 当nums不足2个数时,返回空的res
    if len(nums) < 2 || n < 2 {
        return res
    }
    i,j := 0,len(nums)-1
    if n == 2  {
        for i < j {
            sum := nums[i] + nums[j] 
            if sum < target {
                i++
            } else if sum > target {
                j--
            } else {
                tmp := []int{nums[i], nums[j]}
                res = append(res, tmp)
                // 这后面是为了去除重复数据,
                // 例如,a,a,a,a,b,后面的a+b其实已经重复了,所以只需要偏移,
                // 不需要再次添加到res集合中。
                for i + 1 < j && nums[i+1] == nums[i] {
                    i++
                }
                for j - 1 > i && nums[j-1] == nums[j] {
                    j--
                }
                // 进一步缩小数组的范围,看还存不存在另外一种组合相同
                i++ 
                j--
            }
        }
    } else {
        // 当n > 2时,遍历数组每一个元素,求基于当前数的 n - 1个数之和的全集
        for i := 0; i < len(nums) - 1; i++ {
            arr := nums[i+1:]
            // 数组为去除当前元素,target为减去当前值,n需要减1
            resTmp := findNSum(arr, target-nums[i], n-1)
            // 当找到resTmp结果时,加入结果集
            if len(resTmp) != 0 {
                for _, v := range resTmp {
                    tmp := []int{nums[i]}
                    tmp = append(tmp, v...) // 合并当前数和除去该数之后的其他数集合
                    res = append(res, tmp) // 存入单次计算结果
                }
            }

            // 去除重复数据,相同数字只算一次,例如 [a,a,a,a,b]跳过重复的a到b
            for i + 1 < len(nums) - 1 && nums[i+1] == nums[i] {
                i++
            }
        }
    }
    return res
}

执行结果: