代码随想录训练营Day30 对回溯算法的总结

回溯的本质

为什么要回溯?首先不要把回溯当作是多么nb的算法,纯纯暴力,最多加一点剪枝进行优化而已。

而回溯算法,其实就是在递归函数中嵌套了for循环而已。我们可以想象一个树形结构,递归就是向下深入便利,而for循环是横向遍历。我喜欢将递归说成树枝遍历,for循环说成树层遍历,更好理解些。

对于回溯的理解 

首先附上回溯算法的模板代码:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }
 
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

从代码中我们看出,想要返回上一层结构,要么通过if(终止条件) return,要么就是全部遍历结束自动返回到上一层递归函数代码backtracking()中。

而问题是——对于不同问题需要设置不同的变量,这些变量不会因为我递归的return而自己撤销操作,所以需要我们手动进行撤销。这就是所谓的回溯。

例如:

组合、排列问题中,都需要构造一个path数组来记录路径,而我如果返回到上一层,数组中的元素不会自动撤回,所以需要path.pop_back()来人工删除。

for(int i=startindex;i<=n;i++)
        {
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }

又例如全排列进行树枝去重时,需要构建一个bool数组used来判断元素是否使用过,也不会自动撤回,所以需要重新令used[nums[i]]==false,说明它没有被使用过。

for(int i=0;i<nums.size();i++)
        {
            if(used[i]==true) continue;//用used判断树枝中是否出现了使用过的元素,出现了就跳过
            used[i]=true;
            path.push_back(nums[i]);
            backtracking(nums,used);
            path.pop_back();
            used[i]=false;

}

其实,当我们充分理解这两点后,大部分简单、中等的回溯题都可以解决了。

回溯算法中的statindex是什么意思?

我个人认为就是一个定位的作用,实现递归的深层遍历,是否允许重复元素也决定了startindex的位置i是否需要+1,当然在排列过程中,由于顺序是可以多变的,所以每次都是从第一个元素重新开始递归。

树枝去重和树层去重

树枝去重和树层去重都可以运用到used数组,区别在于树层去重是在给数组排序后,若

if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)  就跳过,continue。

而树枝去重则是

if(used[i]==true) continue;

回溯算法能解决如下问题:

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 棋盘问题:N皇后,解数独等等

Day30打卡成功,耗时1.5小时

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值