算法学习打卡day24|回溯算法初探,组合问题

回溯算法

理论基础

  • 什么是回溯?
    • 回溯法也叫回溯搜索法,就是一种搜索方法,而且还是暴力搜索!
    • 效率问题:由于它属于暴力搜索,本质是穷举,所以它的效率很低,但是有些问题只能穷举,而且普通的暴力算法无法解决,只能使用回溯法。
  • 可以解决什么问题?
    • 组合问题:N个数里面按一定规则找出k个数的集合,这个结果集是无序的,而排列是有序的,比如1和2,2和1在组合里是算一组数据,在排列里算两组。
    • 排列问题
    • 切割问题
    • 子集问题
    • 棋盘问题
  • 如何理解回溯算法?
    • 把回溯算法理解为抽象的树,一般情况下集合数目n为树的宽度,递归的深度为树的深度,本质还是递归,只是要一层一层的去遍历每个子节点,递归结束条件一般是碰到叶子节点结束。

回溯模版

  • 终止条件
if (终止条件) {
	存放结果;
	return;
}
  • 遍历过程
    在这里插入图片描述
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
    处理节点;
    backtracking(路径,选择列表); // 递归
    回溯,撤销处理结果,为下一轮循环元素使用
}

如上代码:for循环相当于横向遍历,递归相当于纵向遍历。

  • 另外回溯法,一般情况下回溯函数返回值为void,结果存到全局的results里或者传的参数里

先解一个组合问题

力扣题目链接
题目描述:
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

思路:

  • 先说一下要定义的东西,上面已经提到了我们需要定义一个全局的results,存符合条件的结果集,然后还要存一个path用来存储每次遍历时的路径,这个path就是要最后放入results的,而回溯也是靠path实现的
  • 直接套用回溯模版,第一层,分别选取1,2,3,4放入path中,然后递归子节点,这里子节点就要比第一层大一个值开始,比如第一层为1,那么递归时就从2开始,2的递归从3开始,依此类推
  • 注意这里是先走完路径1,然后走路径1的递归,再走路径1的递归的递归,然后一步一步回溯,下一轮循环才会走第一层的2,因为递归返回时有回溯步骤,此时路径path已经为空了,所以此种方法不会有重复元素。

代码实现:

    vector<int> path;
    vector<vector<int> > results;
    void backtracking(int n, int k, int start_index) {
        if (path.size() == k) {
            results.emplace_back(path);
            return;
        }
        for (int i = start_index; i <= n; ++i) {
            path.emplace_back(i);
            backtracking(n, k, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) { 
        backtracking(n, k, 1);
        return results;
    }
  • 剪枝优化:
  • 这里可以优化一下,就是我们在横向遍历的时候,当path已经有的元素 + 剩下的元素不足以满足k个数量的时候,我们是不需要继续往下递归了。所以就有 所需节点数:need = k - path.size(),那么只要满足 i < n - need + 1 的时候,才往下遍历,当 i 大于这个值时,剩下的递归就不满足k个了。
vector<int> path;
    vector<vector<int> > results;
    void backtracking(int n, int k, int start_index) {
        if (path.size() == k) {
            results.emplace_back(path);
            return;
        }
        int need = k - path.size();//剪枝优化
        for (int i = start_index; i <= n - need + 1; ++i) {
            path.emplace_back(i);
            backtracking(n, k, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) { 
        backtracking(n, k, 1);
        return results;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值