数据全排列组合

回溯法

通过枚举法,对所有可能性进行遍历。 但是枚举的顺序是 一条路走到黑,发现黑之后,退一步,再向前尝试没走过的路。直到所有路都试过。因此回朔法可以简单的理解为: 走不通就退一步的方枚举法就叫回朔法。而这里回退点也叫做回朔点。

  1. 回朔关键点 通过分析发现,回朔法实现的三大技术关键点分别是:
    一条路走到黑
    回退一步
    另寻他路
  2. 关键点的实现 那么如何才能用代码实现上述三个关键点呢?
    for 循环
    递归
  3. 解释如下
    for循环的作用在于另寻他路: 你可以用for循环可以实现一个路径选择器的功能,该路径选择器可以逐个选择当前节点下的所有可能往下走下去的分支路径。 例如: 现在你走到了节点a,a就像个十字路口,你从上面来到达了a,可以继续向下走。若此时向下走的路有i条,那么你肯定要逐个的把这i条都试一遍才行。而for的作用就是可以让你逐个把所有向下的i个路径既不重复,也不缺失的都试一遍
    递归可以实现一条路走到黑和回退一步: 一条路走到黑: 递归意味着继续向着for给出的路径向下走一步。 如果我们把递归放在for循环内部,那么for每一次的循环,都在给出一个路径之后,进入递归,也就继续向下走了。直到递归出口(走无可走)为止。 那么这就是一条路走到黑的实现方法。 递归从递归出口出来之后,就会实现回退一步。

因此for循环和递归配合可以实现回朔: 当递归从递归出口出来之后。上一层的for循环就会继续执行了。而for循环的继续执行就会给出当前节点下的下一条可行路径。而后递归调用,就顺着这条从未走过的路径又向下走一步。这就是回朔。

说了这么多,回朔法的通常模板是什么呢? 递归和for又是如何配合的呢?


def backward():
    
    if (回朔点):# 这条路走到底的条件。也是递归出口
        保存该结果
        return   
    
    else:
        for route in all_route_set :  逐步选择当前节点下的所有可能route
            
            if 剪枝条件:
                剪枝前的操作
                return   #不继续往下走了,退回上层,换个路再走
            
            else:#当前路径可能是条可行路径
            
                保存当前数据  #向下走之前要记住已经走过这个节点了。例如push当前节点
        
                self.backward() #递归发生,继续向下走一步了。
                
                回朔清理     # 该节点下的所有路径都走完了,清理堆栈,准备下一个递归。例如弹出当前节点

39.组合总和

在这里插入图片描述
回溯

var combinationSum = function(candidates, target) {
   
    let result = [];
    let len = candidates.length;
    // 路径
    let path = [];
    if (len == 0) return result;
    // 优化: 排序数组
    // candidates.sort((a, b) => a-b);
    fn(candidates, 0, target, path);
    console.log(result);
    return result;

    function fn(candidates, start, target, path) {
   
        if (target < 0) return;
        if (target == 0) {
   
            result.push([...path]);
            return;
        } 
        // start避免重复,只向后找
        for(let i=start; i < candidates.length; i++) {
   
            if (target >= candidates[i]) {
   
                path.push(candidates[i]);
                fn(candidates, i, target-candidates[i], path);
                // 回退一步
                path.pop();
            }
        }
    }
};

动态规划

var combinationSum = function(candidates, target) {
   
    let dp = [];
    for(let i = 1; i <= target; i++) {
   
        dp[i] = [];
        for(let j = 0; j < candidates.length; j++) {
   
            if (i == candidates[j]) {
   
                dp[i].push([candidates[j]]);
            }
            if (i > candidates[j]) {
   
                for(let k = 0; k < dp[i-candid
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值