GO实现 组合总和

题目描述

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:
输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
23 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:
输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:
输入: candidates = [2], target = 1
输出: []

提示:
1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都 互不相同
1 <= target <= 500

解析:
给定一个无重复元素数组,一个整数目标值,求数组元素可重复组合的和为目标值的情况。
这个题一上去无脑暴力把所有可能都搞出来也不是不可以,但是感觉会非常麻烦,因此作为一个懒人,当然是想着用一种更方便的方法。第一时间的解决思路就是递归,而所有元素可以重复的条件,代表着每一个元素都可能有不同的路径,所以为了拿到所有情况,需要进行回溯。
go语言做排序略微麻烦,我们假设已经做了排序,那么以实例一candidates [2,3,6,7]来看,我们可以把递归的点定位为目标值减去元素。
在这里插入图片描述
什么意思呢?就像上图所示的那样,我们用7减去数组的第一个元素,当余数大于零,说明我们找到了一个可能组合的一个数,需要找到可以组成余数的其他数,也就是通过递归让我们的目标值减小,当我们找到目标值减去一个元素余数为零时,这条路径的数就是一个符合要求的组合,
以图中示例;
7-2=5
我们就需要再次调用函数找到满足5的组合,因为元素可以重复,所以仍然可以使用2
5-2=3同理往下进行
3-2=1
1-2=-1
当目标值被减完未找到相等情况时,说明这个[2,2,2,2]组合不通,就需要回溯了

递归过程如下:

  1. 遍历数组中的每一个数字。
  2. 递归枚举每一个数字可以选多少次,递归过程中维护一个target变量。如果当前数字小于等于target(相减结果大于零),我们就将其加入我们的路径数组中,相应的target减去当前数字的值。也就是说,每选一个分支,就减去所选分支的值。
  3. 当target == 0时,表示该选择方案是合法的,记录该方案,将其加入res数组中。

这样处理后会有一个问题,方案重复了。为了避免搜索过程中的重复方案,我们要去定义一个搜索起点(index),已经考虑过的数,以后的搜索中就不能出现,让我们的每次搜索都从当前起点往后搜索(包含当前起点),直到搜索到数组末尾,这样就可以避免重复方案了。

代码如下:

func  combinationSum(nums []int,target int) (num [][]int) {
	array:=make([][]int,0)
	if nums==nil || len(nums)==0{
		return array
	}
	getSetOfSum(nums,&array,[]int{},target,0)
	return array
}

func getSetOfSum(nums []int,array *[][]int, b []int,target int,index int)  {
	if target==0{
		tmp := make([]int, len(b))
		copy(tmp,b)
		*array= append(*array,tmp)
		return
	}
	for i:=index;i<len(nums);i++{
		if target<0{
			return
		}
		b=append(b,nums[i])
		getSetOfSum(nums,array,b,target-nums[i],i)
		b=b[:len(b)-1]
	}

}

需要注意的是,go语言函数的参数传递是值拷贝,使用如果使用函数处理,则需要传递存储结果的二维数组的地址,在得到目标数组后添加时还要注意目标数组是动态维护的,因此需要拷贝赋值。

呐,如果使用熟练也可以采用函数类型变量内部递归,减少递归传递的参数,提高空间利用。

func CombinationSum(candidates []int, target int) [][]int {
	var res [][]int
	var path []int
	sum := 0
	var backTrack func(int)
	backTrack = func(start int){
		// 递归终止条件
		// 若当前路径和等于目标和target,将此路径添加到结果集中
		if sum == target{
			temp := make([]int, len(path))
			copy(temp, path)
			res = append(res, temp)
			return
		}
		// 剪枝优化,如果sum已经大于目标和target,就可以返回了,继续遍历没有意义
		if sum > target{
			return
		}
		for i:=start;i<len(candidates);i++{
			sum += candidates[i]
			path = append(path, candidates[i])
			// 因为candidates中的元素可以无限制重复选取,所以这里不再是i+1了
			// 递归
			backTrack(i)
			// 回溯
			sum -= candidates[i]
			path = path[:len(path)-1]
		}
	}
	backTrack(0)
	return res
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昰阳

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值