使用回溯解决leetcode39. 组合总和问题

一、问题(引用力扣39题)

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合

二、回溯

一种使用多层循环并且加入判断减少循环的方法;在力扣的本题的讨论种看到了一个比较通用的模板(这里使用php代码写一下)

function backto(路径,选择路径列表,目标){
    if(满足条件) result[] = 路径;

    for(选择列表){
        做选择
        backto(路径,选择列表,目标(目标可能会在循环的过程种产生变化));
        撤销上面的选择,重新进入到for循环
    }
}
直接上具体代码解释
class CK
{
    public $res=[];

    /**
     * @param Integer[] $candidates
     * @param Integer $target
     * @return array
     */
    function combinationSum($candidates, $target)
    {
        sort($candidates);

        $len = count($candidates);
        $this->back($candidates, $target, 0, $len, $temp_list = []);
        return $this->res;
    }

    /**
     * @param array $candidates  提供选择的列表
     * @param integer $target    目标值
     * @param integer $i         当前再操作选择列表的第几个数(从0开始)
     * @param integer $len      选择列表的长度
     * @param array $temp_list  零时保存我当前的选择组合(也就是上面写的模板的路线)
     * @return mixed
     */
    public function back($candidates, $target, $i, $len, $temp_list)
    {
        if ($target == 0) {
            $this->res[] = $temp_list;
            return;
        }

        for ($start = $i; $start < $len; $start++) {

            if($target - $candidates[$start] < 0) return;

            $temp_list[] = $candidates[$start];

            // 从头开始再扫一遍
            $this->back($candidates, $target - $candidates[$start], $start, $len, $temp_list);

            array_pop($temp_list);

        }

        return;
    }
}

combinationSum 函数中先对提供的选择列表进行了排序,这个对后面的影响不大可以忽略。直接看到back函数,函数一共五个参数,主要的三个参数 $candidates、 $target、 $temp_list,主要解释一下 $temp_list,是用来存储当前做的选择组合,可以看作是一个 篮子
接下来看到

		// 假如选择列表是  [A,B,C,D,E]
		// 从提共的选择列表的第一个开始循环
        for ($start = $i; $start < $len; $start++) {
			// 判断每一个和目标值的大小,只有比目标值小的才可以继续一层循环选择,注意这个地方是下一层,并且目标值也是会变动的,并不是保证原有的,接下来的目标值=原始值-(当前选择组合种的每一个数)
            if($target - $candidates[$start] < 0) return;
			// 把选择列表中的一个数(A)加入到 ---篮子--- 中;
            $temp_list[] = $candidates[$start];

            // 从头开始再扫一遍,这里需要把目标值减去我们上面加入到篮子中的数字,进行接下来的选择,并把新篮子传递进去
            $this->back($candidates, $target - $candidates[$start], $start, $len, $temp_list);
			// 这里是把上面加入到篮子中的数(A)从篮子中拿出来,接下来会继续当前循环走到上面把B加入到篮子中,这就形成了回跳也就是回溯,
            array_pop($temp_list);

        }

然后是函数体中开始的判断

		// 这个判断就是目标值变更成0,也就是篮子字物品的总值等与target初始值,那我们就把这个篮子中物品的组合存起来,回到进入当前函数的循环体中
       if ($target == 0) {
           $this->res[] = $temp_list;
           return;
       }



// ------------------------------------------------------------------

		// 下面是伪代码解释:回到进入当前函数的循环体中
		for ($start = $i; $start < $len; $start++) {
            if($target - $candidates[$start] < 0) return;

            $temp_list[] = $candidates[C];
  			// 假设target=C;并且当前页正好把C放进篮子
           // $target = C-C = 0;说明篮子数值达到了饱和,会直接进入if判断并且return,那就不会进入函数体中的for,所有这里就只写了if,这个时候把篮子组合纪录保存下来后,就会进入下面array_pop(),
           	back([A,B,C,D], $target=C-C, $start, $len, [A,B,C]){
           	   if ($target == 0) {
	           		$this->res[] = $temp_list;
	          	 	return;
     			}
           	}
            
			// 这里会把 C 从篮子中取出,篮子中剩下 [A,B];之后会继续for循环,把D放入到篮子中去判断,
            array_pop($temp_list);
		}
		
这就是我简单的一点思路和理解,回溯的思想还需要不断的练习,主要是练习代码的实现方面。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值