代码随想录Day24 | 回溯

本文详细介绍了回溯理论在解决问题中的应用,包括组合问题的实例,如77.组合问题。作者通过递归和N叉树的角度阐述了回溯算法,以及如何通过剪枝优化查找过程。

回溯理论

文档讲解:代码随想录
视频讲解: 带你学透回溯算法(理论篇)
状态

回溯就是在递归中回溯。所以回溯函数就是递归函数。
我们在二叉树递归中用到回溯就可以发现使用了回溯的方法,都是需要遍历整个二叉树的。所以使用回溯通常就是暴力搜索解决问题

一般解决问题

  1. 组合问题:N个数里面按一定规则找出k个数的集合
  2. 切割问题:一个字符串按一定规则有几种切割方式
  3. 子集问题:一个N个数的集合里有多少符合条件的子集
  4. 排列问题: N个数一定规则全排列,有几种排列方式
  5. 棋盘问题: N皇后、解数独
    关于组合和排列: 组合是集合中的元素不强调元素顺序,排列是强调元素顺序

回溯抽象

一个回溯问题可以抽象为一棵树,这是由于回溯问题就是在集合中递归查找满足条件的子集,一个集合的大小就构成了树的高度,递归查找的次数可以看作树的深度。

回溯模板

  1. 返回值和参数
//一般返回值为void
//参数的确定是需要根据实际逻辑来写的
  1. 终止条件
    可以参考我们遍历树的每条路径的道理,回溯的终止条件就是当现在这个结果满足条件时,存放结果然后返回
if(满足条件)
{
	存放
	return ;
}
  1. 遍历过程
    在这里插入图片描述
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归
    回溯,撤销处理结果
}

这是由于回溯对比二叉树来说,它其实是一个N叉树,所以需要对一层中每个节点进行扫描(for循环),然后再纵向遍历(递归)

77.组合

文档讲解:代码随想录
视频讲解: 带你学透回溯算法-组合问题(对应力扣题目:77.组合)
状态

注意本题是组合,所以是无序的。
从n个数里面取k个,我们将其对应到上方那个n叉树中,第一层根节点整个集合,第二层就是集合取1个数之后的剩余子集和,并且相对左子树取过的数在第二层集合也不需要出现(避免重复,如果是排列就需要出现了),那么第三层就是每个父节点再取1个数,直到取满k个数。
在这里插入图片描述
那么终止条件就是当存储数组的长度达到k时,返回并且存储结果。
单层回溯的逻辑就是,对该层的父节点中的每个元素进行递归调用

class Solution {
private:
vector<int> path;
vector<vector<int>> res;

public:
    void backtracking(int n,int k, int targetIndex)
    {
        //终止条件 path的长度 == k
        if(path.size() == k)
        {
            res.push_back(path);
            return ;
        }
        //单层回溯逻辑
        //遍历一层
        for(int i=targetIndex;i<=n;i++)
        {
            //路径压入当前元素
            path.push_back(i);
            //为当前元素的子节点递归
            backtracking(n,k,i+1);
            //弹出压入的元素,回到父节点,开启下一个元素的搜索
            path.pop_back();
        }
        return ;
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return res;
    }
};

回溯的优化–剪枝

文档讲解:代码随想录
视频讲解: 带你学透回溯算法-组合问题的剪枝操作(对应力扣题目:77.组合)| 回溯法精讲!
状态

对于这道题目,显然当n-i+1<k-path.size()时就不需要进行递归了,因为之后剩余的元素肯定不存在元素个数=k的数组了。
即如果i>n-(k-path.size())+1之后就不可能在后续子树中找到由k个数组合的数组了

 //修改边界条件到达剪枝
        for(int i=targetIndex;i<=n+1-(k-path.size());i++)
        {
            //路径压入当前元素
            path.push_back(i);
            //为当前元素的子节点递归
            backtracking(n,k,i+1);
            //弹出压入的元素,回到父节点,开启下一个元素的搜索
            path.pop_back();
        }
### 关于代码随想录中的回溯算法学习资源 代码随想录是一个专注于帮助初学者掌握数据结构与算法的学习平台,其内容覆盖广泛,尤其适合那些希望系统化提升编程能力的人群[^1]。对于回溯算法这一重要主题,代码随想录提供了详尽的讲解和实践案例。 #### 回溯算法简介 回溯法是一种经典的递归求解方法,在解决组合问题、排列问题以及子集问题等方面具有广泛应用。它的核心思想是通过逐步构建解决方案并尝试每一步可能的选择来探索所有可能性。如果发现当前路径无法满足条件,则立即退回上一状态重新选择其他分支继续试探。这种方法能够有效避免穷举所有的候选方案从而提高效率。 #### 代码随想录中的回溯算法教学特点 在代码随想录里,有关回溯算法的内容通常被安排在一个较为系统的框架下进行讲授: - **理论基础**:从基本概念出发介绍什么是回溯法及其适用场景; - **经典题目解析**:选取LeetCode上的典型例题作为练习素材,比如全排列问题、N皇后问题等; - **实战演练**:鼓励读者跟随视频或者文档一步步完成编码过程,并提供详细的错误分析指导; 以下是基于Python语言实现的一个简单版本的全排列生成器示例: ```python def permute(nums): result = [] # 辅助函数用于执行实际的回溯逻辑 def backtrack(path, options): if not options: result.append(path[:]) # 当选项为空时记录当前路径到最终结果列表中 return for i in range(len(options)): chosen = options[i] path.append(chosen) # 做出选择 remaining_options = options[:i]+options[i+1:] backtrack(path,remaining_options) # 继续深入下一个决策节点 path.pop() # 撤销刚才所做的决定以便测试新的方向 backtrack([],nums) return result ``` 此段程序展示了如何运用栈的思想去模拟整个搜索树的过程,其中`backtrack()`负责管理每一次的状态转移直至找到符合条件的结果为止。 #### 推荐参考资料链接 虽然上述提到的具体章节尚未更新完毕[^3],但是可以参考官方博客或者其他社区贡献者分享的相关文章进一步加深理解。同时也可以关注作者Carl发布的最新动态获取更多优质课程信息。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值