【回溯算法题记录】组合问题汇总

77. 组合

题目链接

题目描述

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

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

题目分析

先放上回溯模板:

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

我们用vector<int> vec来存放每一个答案vector<vector<int>> result来存放全部的答案

那么终止条件就是:vec中的元素等于题目规定的k时,就可以把当前vec存放到result中了。也就是:

if(vec.size() == k){
            result.push_back(vec);
            return;
        }

整体范围为[1, n]的集合,而我们在进行递归时需要选择该层处理的元素子集,也就是范围是[j, n]的子集,1<=j<=n。

for(int i = j; i <= n; i++){
            vec.push_back(i);
            backtracking(i+1, k, n, vec, result);
            vec.pop_back();
        }

其实看下面这张图,就能理解回溯的逻辑:
在这里插入图片描述
pop操作实际上可以看作:例如从结果集合[1, 2]返回到上层的在2,3,4中取一个数[2, 3, 4]这个集合中在进行处理,也就是进行取3操作。

整体cpp代码

class Solution {
public:
    void backtracking(int j, int k, int n, vector<int>& vec, vector<vector<int>>& result){
        if(vec.size() == k){
            result.push_back(vec);
            return;
        }

        for(int i = j; i <= n; i++){
            vec.push_back(i);
            backtracking(i+1, k, n, vec, result);
            vec.pop_back();
        }
    }

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

216. 组合总和 III

题目链接

题目描述

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:

  • 只使用数字1到9
  • 每个数字 最多使用一次
    返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。

题目分析

其实和上面一样,只不过多了一个总和为n

cpp代码

class Solution {
public:
    void backtracking(int m, int k, int n, vector<int>& vec, vector<vector<int>>& result){
        // 如果当前路径元素个数为k,且和为n,则放入答案集
        if(vec.size() == k){
            if(accumulate(vec.begin(), vec.end(), 0) == n){
                result.push_back(vec);
            }
            return;
        }

        // 从m开始处理
        for(int i = m; i <=  9; i++){
            vec.push_back(i);   // 将当前元素放入路径
            backtracking(i+1, k, n, vec, result);
            vec.pop_back(); // 回溯
        }

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

17. 电话号码的字母组合

题目链接

题目描述

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

题目分析

这里涉及到在多个集合中进行组合。
通过画图来分析,例如给定数字串string digits = "23",我们建立对应的字母集vector<string> digits_to_abc = ["abd", "def"]

在这里插入图片描述

  • 我们首先从'2'对应的字母集"abc"中选取一个字母;
  • 然后再在'3'对应的字母集"def"中选取一个字母;
  • 每次只选一个字母,并且每个数字的字母集都要进行选取操作。那么我们一条路径走完就是要在每个字母集中选取完,这里我们设置一个int index来表示当前应该选取的字母集,那么路径完成的判断语句可以写成:
if(index == digits_to_abc.size()){
	results.push_back(s);
	return;
}

一条路径string s走完后,我们将当前记录的路径放到最终的结果vector<string> results中。

那么如何进行回溯呢?
我们拿最左边"ad"这条路径作为例子来分析:
在这里插入图片描述
我们当前路径是s = "ad",通过判断发现index已经指向了最后一个字母集,没有字母集可以选择了,那这个时候我们就要回到上一个字母集去选择另一个字母(另一条路径),那么就要把当前路径中在最后一个字母集选取的字母pop出来,这样就完成了回溯。

cpp代码

class Solution {
public:
    // 回溯的参数包括:我们根据输入digits建好的字母数组,vec是每一层回溯的结果,results放置所有答案
    void backtracking(int index, vector<string> digits_to_abc, string& s, vector<string>& results){
        if(index == digits_to_abc.size()) {
            results.push_back(s);
            return;
        }
        
        string string_letter = digits_to_abc[index];
        for(int i = 0; i < string_letter.size(); i++){
            s += string_letter[i];
            backtracking(index + 1, digits_to_abc, s, results);
            s.pop_back();
        }
    }

    vector<string> letterCombinations(string digits) {
        vector<string> results;
        if(digits.size() == 0) return results;
        cout << "done1" << endl;

        // 先构造每个数字对应的字母组合
        vector<string> temp;
        temp.push_back("abc");
        temp.push_back("def");
        temp.push_back("ghi");
        temp.push_back("jkl");
        temp.push_back("mno");
        temp.push_back("pqrs");
        temp.push_back("tuv");
        temp.push_back("wxyz");
        cout << "done2" << endl;
        
        // 建一个string的vector来存放digits对应的字母
        vector<string> digits_to_abc;   
        for(char c : digits){
            digits_to_abc.push_back(temp[c-'2']);
        }

        // 打印看看是不是对应的
        cout << "输入digits:" << digits << endl;
        for(string s : digits_to_abc){
            cout << s << endl;
        }

        // 进行回溯
        string s;
        backtracking(0, digits_to_abc, s, results);

        return results;
    }
};
  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值