【代码随想录|回溯算法组合问题】

代码随想录文章链接代码随想录

回溯算法理论

一、回溯法解决的问题

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

二、集合的宽度一般用for循环,集合的深度用递归

三、回溯算法模板

一般情况下用

void backtracking(参数)

参数一般比较多,遇到再往里加 

终止条件

if (终止条件) {巴拉巴拉 return;}

组合、切割、排列、棋盘问题一般在叶子节点上收割结果,子集问题是在每一个结点上都要搜集结果。

对节点的搜集处理

 for (集合中元素) {
        处理节点;
        递归;
        回溯,撤销处理结果;
    }

回溯算法三道题 

77.组合

题目链接https://leetcode.cn/problems/combinations

这里我自己理解的递归顺序呢是从(2,4,1)开始往后递归,开始进入for循环里面的backtracing那就是在对1进行组合了,

for循环i=1结束时 result 里面就应该已经有<1,2><1,3><1,4>了,然后我pop掉1进入

for循环i=2排序好 result 里面添加<2,3><2,4>,

for循环i=3往result里面添加<3,4>,

for循环i=4本来准备继续添加5的,但是5>n了所以这里因为是void就会直接返回给4了,然后我的4也被pop掉,

for循环结束最终这个函数返回给调用的(2,4,1)。

class Solution {
public:
    vector<int> path;//能满足条件的排列数组
    vector<vector<int>> result;//放满足的结果数组
    void backtracing(int n, int k, int startindex) {
        if (k == path.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startindex; i <= n; i++) {
            path.push_back(i);
            backtracing(n, k, i + 1);
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        backtracing(n, k, 1);
        return result;
    }
};

216.组合总和

题目链接https://leetcode.cn/problems/combination-sum-iii

这里可以进行剪枝,一个剪枝是在sum>要求的n就可以返回了一个是在for循环里面i<=9-(k-path.size())+1,就是我现在可以还需要取k-path.size()那么多个,其实就跟刚刚那道题一样,刚刚那道题for循环改成i<=4-(k-path.size())+1有一个数据也是对的,一共4个数我选两个我递归执行到3就足够了,因为到4也添加不了新的结点最终也会被pop掉,但是因为这道题是要从1到9里面选嘛,除开我的这个节点在剩余节点中选就是i<=9-(k-path.size())+1

class Solution {
public:
vector<int>path;
vector<vector<int>> result;
 void backtracking(int k,int n,int sum,int startindex){
    if(sum>n)return;//剪枝
    if(path.size()==k){
        if(sum==n){
            result.push_back(path);
        }
        return;
    }
    for(int i=startindex;i<=9-(k-path.size())+1;i++){//剪枝,i<=n也对
        path.push_back(i);
        sum+=i;
        backtracking(k,n,sum,i+1);
        sum-=i;
        path.pop_back();
    }
 }
    vector<vector<int>> combinationSum3(int k, int n) {
        backtracking(k,n,0,1);
        return result;
    }
};

17.电话号码的字符组合

题目链接https://leetcode.cn/problems/letter-combinations-of-a-phone-number

这里传入digits的大小就是我要每一个我要放进result的s的长度,也就是我遍历的深度,所以这里的递归终止条件是当我的index的大小和遍历的深度相等的时候我才添加结果集:

 if (index == digits.size())

 然后我遍历的宽度应该是这个数组所对应字符串的长度:

for (int i = 0; i < letter.size(); i++)

递归的顺序呢 比如我传入的是(2,3),[对应的字符"abc", "def"]  我先求出这个字符数组的第一个数所对应的字符串letter,然后我对letter的每个数进行递归:(这里的index指的每一个数字嘛(它只会遍历到深度的大小),所以我在进行 backtracking(digits, index + 1);的时候就是对第二个字符串进行选择了)

for循环i=0结束时 result 里面就应该已经有<a,d><a,e><a,f>,然后我pop掉a进入

for循环i=1排序好 result 里面添加<b,d><b,e><b,f>

for循环i=2往result里面添加<c,d><c,e><c,f>

for循环i=3已经不满足条件i<letter.size(),也就是i<3,直接返回到调用的 backtracking(digits, 0);返回result数组。

class Solution {
public:
    string s;
    vector<string> result;
    string lettermap[10] = {"", "", "abc", "def", 
"ghi", "jkl", "mno", "pqrs", "yuv", "wxyz"};
//虽然传入的数只从2到9,但是为了和下标一一对应要把0和一设置成""
    void backtracking(string digits, int index) {
        if (index == digits.size()) {//结束条件是index指向的数已经到空了才加到结果集
            result.push_back(s);
            return;
        }
        int digit = digits[index] - '0';
        string letter = lettermap[digit];
        for (int i = 0; i < letter.size(); i++) {
            s.push_back(letter[i]);
            backtracking(digits, index + 1);
            s.pop_back();//回溯
        }
    }
    vector<string> letterCombinations(string digits) {
        if (digits.size() == 0)
            return result;
        backtracking(digits, 0);
        return result;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值