Golang-回溯-组合总和

每日力扣-组合总和

今天作为小菜菜的我花了好长时间在这类题型上,做了组合总和组合总和Ⅱ,花的时间长不可怕,重要的是能领悟,能学到东西!!
这类题需要尝试将数组的元素进行各种组合以至于累加到目标数,很明显题意告诉我们要用递归回溯的思想,进行不断的尝试不断地累加不断地回溯,这样肯定有一定地效率损失,所以要做到一定地优化,我目前知道地优化貌似就是:剪枝,剪去不必要地递归,提升效率~说实话要不是做题我都不知道剪枝是个什么东西,所以实践多做题很重要,有利于扩阔视野,长见识。

接招:组合总和Ⅰ

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:
输入:candidates = [2,3,6,7], target = 7,
所求解集为:
[ [7], [2,2,3]]
示例 2:
输入:candidates = [2,3,5], target = 8,
所求解集为:
[ [2,2,2,2],[2,3,3], [3,5]]

答:这道题中重要都字眼我已经标粗了,就是数组中的元素可以重复使用,比如例子中的 [2,3,6,7],可以通过下面的图很清晰的表示:
在这里插入图片描述
右边向下一个元素递归,而左边继续以本元素递归,然后根据条件退出递归回溯到上一级继续尝试不同的组合。以下我们看代码,各步解析写在注释中,为了方便看注释,把反斜杠反过来了~

func combinationSum(candidates []int, target int) [][]int {
    sort.Ints(candidates)  \\排序一下数组,方便剪枝操作
    var res [][]int
    var temp []int
    var dfs func(int,int)
    dfs = func(target,index int){ \\递归方法,根据目标值和数组下标递归
        \\成功案例结果 刚好target = 0,所以该组合成立
        if target == 0{
            res = append(res,append([]int(nil),temp...)) \\不可以直接temp不然结果错乱是同一个切片
            return
        }
        \\退出递归条件:到数组末尾
        if index == len(candidates){
            return
        }
        \\剪枝:如果数组中有重复数,重复的那个直接跳过不考虑,因为数组中的数可以重复使用,重复的数没有意义
        if index > 0 && candidates[index] == candidates[index-1] {
            return
        }
        \\开始递归:使用本身或者使用数组下一个数
        if target - candidates[index] >= 0{         \\大于等于0说明可能可以组成一种结果
            temp = append(temp,candidates[index])   \\将该结果保存到临时数组里
            dfs(target-candidates[index],index)     \\使用本数index递归,但target是他们的差,因为本数已经是temp的一个解
            temp = temp[:len(temp)-1]               \\剔除该数,恢复到原状,换另外一种情况尝试
        }else{  \\剪枝:由于数组排序过,如果当前index的数大于了target,那数组后面的数也同样大于,无需再考虑!
            return
        }
        \\本身数尝试完就测试数组下一个数
        dfs(target,index+1)
    }
    \\从0下标开始递归
    dfs(target,0)
    return res
}

我觉得我注释解析写的挺清楚的,希望你看得懂~

再接招:组合总和Ⅱ

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[ [1, 7],[1, 2, 5],[2, 6], [1, 1, 6]]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5,
所求解集为:
[ [1,2,2],[5]]

答:这道题和组合总和Ⅰ一样利用递归回溯来实现,但是它的结果集中,数组的每个数只能用一次,所以面对数组中有多个重复数的情况,我们要考虑的是去重!否则只按Ⅰ的思路来结果会有重复值,因为比如2,5,2,1,2,target是5,1可以和其中三个2随机组成5,结果是5,并且利用数组元素不重复,但是偏偏结果集是重复的,所以首要任务去重,具体做法就是在Ⅰ的基础上,判断以当前下标为起点,如果刚刚用的数下一个数仍然是当前数,那就直接跳过。比如1刚刚和2组完队,for循环下一个的时候还是2,那就直接跳过~来看代码:

func combinationSum2(candidates []int, target int) [][]int {
    sort.Ints(candidates)
    var res [][]int
    var temp []int
    var dfs func(int,int)
    dfs = func (target,index int){
        if target == 0{
            res = append(res,append([]int(nil),temp...))
            return
        }
        \\前面代码没多啥变化
		
		\\考虑的就是比如1可以和多个2组成一组,但不可以分别和多个2组成一组,因为属于重复
        for i:=index;i<len(candidates);i++{   \\通过for循环来往下递归
        	\\剪枝:如果当前数和上一个数相同,上一个数是组过队了,所以没必要在组一次
            if i-1 >= index && candidates[i] == candidates[i-1]{
                continue
            }
            if target - candidates[i] >= 0{
                temp = append(temp,candidates[i])
                dfs(target-candidates[i],i+1)
                temp = temp[:len(temp)-1]
            }else{\\剪枝:对于排序过的数组,当前数大于target后面就不用比了
                return
            }
        }
    }
    dfs(target,0)
    return res
}

看到这里,今天的文就结束咯
感谢观看,要记得每天都学习喔~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值