代码随想录刷题攻略---回溯法1--组合问题

例题:

这题可以使用k层for循环去穷举,但一旦k值变大,for循环的嵌套就变得麻烦。

故 对于这种复杂的组合问题(以及子集、分割、排列)问题,光用for循环嵌套有可能无法得出解。

这时,我们使用回溯(搜索法)来做层叠嵌套(相当于k层for循环),每一次递归中(回溯是递归的副产品,有递归就会有回溯,我们说的回溯法一般也指的是递归法)嵌入一个for循环,就可以解决多层for循环的嵌套问题了(这里可以看出在本质上,递归也是一种穷举)。


 回溯三部曲:

1.递归函数的参数以及返回值

递归函数的参数包含所有需要在递归过程中处理的参数,在这题中,我们要把值n和取值个数k传入回溯函数,以及一个index变量,这个参数用来记录本层的递归中,集合从哪里开始遍历(这题中集合就是[1,...,n] )。

2.递归结束的条件

每一层递归何时结束,就是递归结束条件决定的,一般是如图这种形式:

if (终止条件) {
        存放结果;
        return;
}

在这题中,当数组里存放的数字超过k个,这一层的递归就结束了。

3.单层循环的逻辑:

对每次循环的行为进行定义。一般来说,在每层递归中,我们需要对余下数据(从index+1的地方收集数据,构成组合)进行处理,即把它们加入数组中组成不同的组合直到该路径的数组大小=k,每收集够足够数量的数据,我们将其保存后寻找下一个组合(即把数组的元素弹出,放入新数据),格式一般如图:

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

code: 

lass Solution {
public:
        vector<vector<int>> result;
        vector<int> path;

        vector<vector<int>> combine(int n, int k) {
        backtracing(n,k,1);
        return result;
    }

    void backtracing(int n,int k,int index)
    {
        if(path.size() == k)//终止条件+存放结果
        {
            result.push_back(path);
            return ;
        }
        //单层搜索逻辑
        for(int i=index;i <= n;i++)
        {
            path.push_back(i);
            backtracing(n,k,i+1);
            path.pop_back();
        }
    }
};

剪枝优化:

若k=4,且当前index到n的范围里的数字已经小于4了,这时若再进行逐一遍历已经没有意义了,平白无故还耗费了时间开销,若我们可以对剩下的数据进行判断,减少无畏的遍历过程,就是剪枝。上述情况发生在for循环体,我们可以这样解决,如图:

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) // i为本次搜索的起始位置

 这样就减少了不必要的开销,加快循环处理的过程


相似题目:

这题的不同之处在于多了一些条件,比如目标和值为n,这个时候我们在回溯过程中要记录当前的和值sum,当sum=n并且存储的数据大小为k时,符合我们的要求,才将这组数据加入result数组中 。所以在回溯循环体中,当回溯返回上一级循环时,我们需要把sum也进行减去的操作

 code:

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;

    vector<vector<int>> combinationSum3(int k, int n) {
        backtracing(n,k,1,0);
        return result;
    }

    void backtracing(int n,int k,int index,int sum)
    {
        if(path.size() == k)
        {
            if(n == sum) result.push_back(path);
            return ;
        }

        for(int i=index;i<=9;i++)
        {
            path.push_back(i);
            sum += i;
            backtracing(n,k,i+1,sum);
            sum -= i;
            path.pop_back();
        }
    }
};

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值