【回溯】

(题库来自leetcode,题解源自代码随想录

前言:

对于组合问题:

大多可以用回溯算法解决。通过暴力回溯所有可能的组合,再根据题意保存正确的组合即可。(对于元素重复使用,数组是否有重复元素,有细节需要注意)
在回溯的基础上,可以用记忆化搜索和枝剪等操作优化
如果是求组合的个数,可以考虑动态规划

对于全排列问题:

同样也是用回溯算法。需要注意数组是否有重复元素。

对于分割问题:

类似于组合问题,不过组合的是分割线的位置。递归所有的分割线组合,根据分割线对子串进行判断,保存符合题意的结果。

对于子集问题:

也类似组合问题。相对于是求递归中所有的节点。组合问题相对于求满足结果的叶子节点。

回溯算法的模板(参考卡哥)

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

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

相当于是n叉树,整个递归过程如下:

在这里插入图片描述


组合

[题]

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案。
1 <= n <= 20
1 <= k <= n

[例]

输入:n = 4, k = 2
输出:[[2,4], [3,4],[2,3], [1,2], [1,3],[1,4],]

[思路]

回溯法的搜索过程就是一个树型结构的遍历过程,在如下图中,可以看出for循环用来横向遍历,递归的过程是纵向遍历。在这里插入图片描述
输入参数: n, k, startindex(为了使元素不重复,下一层for循环的开始一个为上一层的startindex+1)
终止条件: 当路径内元素个数==k时,说明已经是一个符合题意得组合。

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void backtracking( int n, int k, int startindex)
    {
        if(path.size() == k)
        {
            res.push_back(path);
            return;
        }
        for(int i = startindex; i<=n-(k-path.size())+1; ++i)//i<=n-(k-path.size())+1这里做了一个枝剪,因为剩下得元素如果不够凑成一个组合的话就没必要列举了
        {
            path.push_back(i);
            backtracking(n, k, i+1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return res;
    }
};

组合总和Ⅰ(无重复元素,可重复取)

[题]

给你一个无重复元素的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的所有不同组合,并以列表形式返回。你可以按任意顺序返回这些组合。
candidates 中的同一个 数字可以无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。

[例]
[思路]

本题和上一题没太大区别,只不过增加了要求:元素可以重复选取。
整个回溯遍历过程如下图所示:
在这里插入图片描述
输入参数: n, k, startindex(为了使元素可以重复,下一层for循环的开始一个为上一层的startindex)
终止条件: 当路径内元素个数==k时,说明已经是一个符合题意得组合。

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(int n, int sum, int & target,vector<int> & candidates, int start)
    {
        if(n>=150||sum>target)//如果组合数大于等于150或者和大于target说明该组合不满足
        {
            return;
        }
        if(sum == target)//和等于target的组合保存起来
        {
            res.push_back(path);
        }
        for(int i = start; ii < candidates.size() && sum + candidates[i] <= target; i++)//同样的枝剪
        {
            path.push_back(candidates[i]);
            backtracking(n+1, sum+candidates[i], target, candidates, i);//这里i不加一,因为元素可以重复
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        res.clear();
        path.clear();
        backtracking(1,0,target,candidates,0);
        return res;
    }
};

组合总和Ⅱ(有重复元素,不可重复取)

[题]

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。

[例]

输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:[[1,1,6],[1,2,5],[1,7],[2,6]]

[思路]

本题与上一题组合总和Ⅰ,它要求每个元素在同一组合中不可重复使用。那似乎跟这题组合一样?
实际上是不一样。因为该题没有保证给定的数组candidates没有重复元素。而前面两题都是保证了元素是没有重复的。
元素重复会带来一个问题:组合可能会得到多个相同的组合。所以,本题的关键是:如何保证同一层不会遍历到相同大小的元素。
有个简单的想法:先对candidates数组排序,在遍历同一层的时候,与前一个元素比较。如果相同就直接跳过。

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;

    void backtracking(int &target, vector<int>& candidates, int sum, int start)
    {
        if(sum > target)
        {
            return;
        }
        if(sum == target)
        {
            res.push_back(path);
            return;
        }
        for(int i=start; i < candidates.size() && sum + candidates[i] <= target; i++)
        {
           if (i > start && candidates[i] == candidates[i - 1]) {//同一层相同元素直接跳过
                continue;
            }
            path.push_back(candidates[i]);
            backtracking(target, candidates, sum+candidates[i], i+1);//因为不能重复取同一元素,i+1.
            path.pop_back();
        }
    }
    
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        res.clear();
        path.clear();
        sort(candidates.begin(), candidates.end());
        backtracking(target, candidates, 0, 0);
        return res;
    }
};

组合总和Ⅲ(无重复元素,不可重复取)

[题]

找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9
每个数字最多使用一次
返回所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
2 <= k <= 9
1 <= n <= 60

[例]

输入: k = 3, n = 7
输出: [[1,2,4]]
解释:
1 + 2 + 4 = 7
没有其他符合的组合了。

[思路]

本题和组合几乎是一致的。只是增加了要求,只能使用数字1~9。反而更简单。
可以用组合一样的方法做本题。我在这个基础上,我把层数k也加入到了回溯中,便于更好理解整个递归的过程。

 void backtracking(int k ,int n, int sum, int start)

递归到最后一层,判断sum是否为n即可。

if(k==0)
{
   if(sum == n) res.push_back(path);
   return;
}

整个回溯过程如下:
在这里插入图片描述

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(int k ,int n, int sum, int start)
    {
        if(sum>n) return;
        if(k==0)
        {
            if(sum == n) res.push_back(path);
            return;
        }
        for(int i=start; i<=9-k+1; i++)
        {
            
            path.push_back(i);
            backtracking(k-1,n, sum+i, i+1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        res.clear();
        path.clear();
        backtracking(k,n,0,1);
        return res;
    }
};

组合总和Ⅳ(可重复取,排列数)

[题]

给你一个由不同整数组成的数组 nums,和一个目标整数target。请你从 nums 中找出并返回总和为 target 的元素组合的个数。
题目数据保证答案符合 32 位整数范围。
1 <= nums.length <= 200
1 <= nums[i] <= 1000
nums 中的所有元素互不相同
1 <= target <= 1000

[例]

输入:nums = [1,2,3], target = 4
输出:7
解释:
所有可能的组合为:(1, 1, 1, 1)(1, 1, 2)(1, 2, 1)(1, 3)(2, 1, 1)(2, 2)(3, 1)
请注意,顺序不同的序列被视作不同的组合。

[思路]

本题与前面几题有个明显区别是,本题只求组合的个数,而不要求列出所有组合,并且求的是排列。
因此可以考虑动态规划——完全背包。完全背包的问题可以看我另一篇博客。
设计一个dp数组:dp[i]表示凑成目标正整数为i的排列个数。
递推公式:dp[i] += dp[i - nums[j]]。
遍历顺序:求排列数就是外层for遍历背包,内层for循环遍历物品。

需要指明的是:回溯算法也可解决这个问题。因为回溯的本质就是穷举。只要列出所有的排列,再判断和是否为目标数即可。但是必然会消耗很大的内存和运行时间,本题就会超时。有个优化办法是,用一个map记忆每次回溯的结果。如果最后遇到同样的情况直接用map记录的结果,而不用多次递归。也就是记忆化搜索
关于如何用回溯求所有排列将在下一题讲解。

[代码]
class Solution {//动态规划
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> dp(target+1, 0);
        dp[0] = 1;
        for(int i = 0; i<=target; i++)
        {
            for(int j = 0; j<nums.size();j++)
            {
                if(nums[j]<=i&&dp[i] < INT_MAX - dp[i - nums[j]]) 
                dp[i] = dp[i]+dp[i-nums[j]];
            }
        }
        return dp[target];
    }
};
class Solution {//记忆化搜索
public:
    unordered_map<int,int> m;
    int combinationSum4(vector<int>& nums, int target) {
        int res=0;
        res=dfs(target,nums);
        return res;
    }
    int dfs(int& target,vector<int>& nums){
        if(target==0){
            return 1;
        }
        if(target<0){
            return 0;
        }
        if(m.count(target)){//如果m.count(target)不为0,说明之前递归有过同样情况。直接使用之前递归的结果
            return m[target];
        }
        int counting=0;
        for(int i=0;i<nums.size();i++){//这里i是从0开始遍历,因为求的是排列。
            target-=nums[i];
            counting+=dfs(target,nums);
            target+=nums[i];
        }
        m[target]=counting;//保存每次递归的结果
        return counting;
    }
};

全排列

[题]

给定一个不含重复数字的数组 nums ,返回其所有可能的全排列。你可以按任意顺序返回答案。
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同

[例]

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

[思路]

这题几乎和组合一样。但排列强调元素的顺序,所以在for循环中的start索引必须从0开始。这样在遍历后面的元素时,在递归到下一层之后也可以再遍历到前面的元素。从而保证所有顺序的组合都被列举到。
这就有个问题:如何保证递归到下一层后,不会遍历到之前层用过的元素?
所以我们要记录同一树枝下,使用了哪些元素,当递归过程中遇到该元素就直接跳过。

for (int i = 0; i < nums.size(); i++) {//每一层的i从0开始
    if (used[i] == true) continue; // path里已经使用过的元素,直接跳过
    used[i] = true;//把当前使用的元素标记为true
    path.push_back(nums[i]);
    backtracking(nums, used);//used数组递归到下一层
    path.pop_back();
    used[i] = false;//该层递归结束后,把元素重新标记为false
}

整个过程如下:
在这里插入图片描述

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void trackbacking(vector<int> & nums,int* map)
    {
        if(path.size() == nums.size()) 
        {
            res.push_back(path);
            return;
        }
        
        for(int i=0; i<nums.size(); i++)
        {
            if(map[i] == 1)//如果nums[i]已经使用过则跳过该元素
            {
               continue;
            }
            path.push_back(nums[i]);
            map[i] = 1;//标记nums[i]已经使用过
           	trackbacking(nums,map);//递归到下一层
            map[i] = 0;//回溯
            path.pop_back();   
        }
    }
public:
    vector<vector<int>> permute(vector<int>& nums) {
        res.clear();
        path.clear();
        int map[7] = {0};//因为nums最多6个元素,使用用7个int足够记录
        trackbacking(nums, map);
        return res;
    }
};

全排列Ⅱ(有重复元素)

[题]

给定一个可包含重复数字的序列 nums ,按任意顺序返回所有不重复的全排列。
1 <= nums.length <= 8
-10 <= nums[i] <= 10

[例]

输入:nums = [1,1,2]
输出: [[1,1,2], [1,2,1], [2,1,1]]

[思路]

区别与全排列,本题的nums可能有重复元素。
所以,对于同一层的递归,不能使用相同大小的元素。而在某一递归路径上,可以使用相同大小的元素,但不能使用同一个元素。
如图:
在这里插入图片描述

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void trackbacking(vector<int> & nums,int* map)
    {
        if(path.size() == nums.size()) 
        {
            res.push_back(path);
            return;
        }
        for(int i=0; i<nums.size(); i++)
        {
            if(i>0 && nums[i-1] == nums[i] && map[i-1]==0)//同一层去重
            {
                continue;
            }
            if(map[i] == 0)//同一树枝去重
            {
                path.push_back(nums[i]);
                map[i] = 1;
                trackbacking(nums,map);
                map[i] = 0;
                path.pop_back();
            } 
        }
    }
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        res.clear();
        path.clear();
        sort(nums.begin(), nums.end());
        int map[8] = {0};
        trackbacking(nums, map);
        return res;
    }
};

分割回文串

[题]

给你一个字符串 s,请你将 s 分割成一些子串,使每个子串都是 回文串 。返回 s 所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。

[例]

输入:s = “aab”
输出:[[“a”,“a”,“b”],[“aa”,“b”]]

[思路]

类似组合问题:
组合问题:选取一个a之后,在bcdef中再去选取第二个,选取b之后在cdef中再选取第三个…。
切割问题:切割一个a之后,在bcdef中再去切割第二段,切割b之后在cdef中再切割第三段…。
整个递归过程如图:
在这里插入图片描述
**单层逻辑:**通过for循环的startindex和i来分割字符串。下一层startindex从i+1开始。
**终止条件:**如果startindex>= s.size(), 说明已经没有子串可以分割,保存结果并返回。

[代码]
class Solution {
private:
    vector<vector<string>> result;
    vector<string> path; // 放已经回文的子串
    void backtracking (const string& s, int startIndex) {
        // 如果起始位置已经大于s的大小,说明已经找到了一组分割方案了
        if (startIndex >= s.size()) {
            result.push_back(path);
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isPalindrome(s, startIndex, i)) {   // 是回文子串
                // 获取[startIndex,i]在s中的子串
                string str = s.substr(startIndex, i - startIndex + 1);
                path.push_back(str);
            } else {                                // 不是回文,跳过
                continue;
            }
            backtracking(s, i + 1); // 寻找i+1为起始位置的子串
            path.pop_back(); // 回溯过程,弹出本次已经填在的子串
        }
    }
    bool isPalindrome(const string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
public:
    vector<vector<string>> partition(string s) {
        result.clear();
        path.clear();
        backtracking(s, 0);
        return result;
    }
};

复原ip地址

[题]

有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:“0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、“192.168.1.312” 和 “192.168@1.1” 是 无效 IP 地址。
给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。

[例]

输入:s = “25525511135”
输出:[“255.255.11.135”,“255.255.111.35”]

[思路]

这题跟分割回文回文串很像。不过题目要求正好由四个整数组成。所有对递归的层数有要求。
其他都是一样。遍历分割点,判断子串是否满足要求,满足就递归到下一层。第二层做同样的操作。直到递归到第四层,保存结果。
这题难的是对字符串的操作。

[代码]
vector<string> result;// 记录结果
    // startIndex: 搜索的起始位置,pointNum:添加逗点的数量
    void backtracking(string& s, int startIndex, int pointNum) {
        if (pointNum == 3) { // 逗点数量为3时,分隔结束
            // 判断第四段子字符串是否合法,如果合法就放进result中
            if (isValid(s, startIndex, s.size() - 1)) {
                result.push_back(s);
            }
            return;
        }
        for (int i = startIndex; i < s.size(); i++) {
            if (isValid(s, startIndex, i)) { // 判断 [startIndex,i] 这个区间的子串是否合法
                s.insert(s.begin() + i + 1 , '.');  // 在i的后面插入一个逗点
                pointNum++;
                backtracking(s, i + 2, pointNum);   // 插入逗点之后下一个子串的起始位置为i+2
                pointNum--;                         // 回溯
                s.erase(s.begin() + i + 1);         // 回溯删掉逗点
            } else break; // 不合法,直接结束本层循环
        }
    }
    // 判断字符串s在左闭又闭区间[start, end]所组成的数字是否合法
    bool isValid(const string& s, int start, int end) {
        if (start > end) {
            return false;
        }
        if (s[start] == '0' && start != end) { // 0开头的数字不合法
                return false;
        }
        int num = 0;
        for (int i = start; i <= end; i++) {
            if (s[i] > '9' || s[i] < '0') { // 遇到非数字字符不合法
                return false;
            }
            num = num * 10 + (s[i] - '0');
            if (num > 255) { // 如果大于255了不合法
                return false;
            }
        }
        return true;
    }
public:
    vector<string> restoreIpAddresses(string s) {
        result.clear();
        if (s.size() < 4 || s.size() > 12) return result; // 算是剪枝了
        backtracking(s, 0, 0);
        return result;
    }

子集

[题]

给你一个整数数组 nums ,数组中的元素互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
1 <= nums.length <= 10
-10 <= nums[i] <= 10

[例]

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

[思路]

就是组合问题。如果把回溯看成是一棵n叉树。组合问题就是求叶子节点的问题。而子集问题就是求所有节点的问题。只需要在递归过程中,保存所有的节点就行。

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>&nums, int start)
    {
        if(start>= nums.size()) return;

        for(int i =start; i<nums.size(); i++)
        {
            
            path.push_back(nums[i]);
            res.push_back(path);
            backtracking(nums, i+1);
            path.pop_back();

        }
    }
    
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        res.clear();
        path.clear();
        res.push_back(path);
        backtracking(nums, 0);
        return res;
    }
};

子集Ⅱ(有重复元素)

[题]

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

[例]

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

[思路]

跟含有重复元素的数组求组合一样。只需要数层去重就行。

[代码]
class Solution {

    vector<vector<int>> res;
    vector<int> path;
    void backtracking(vector<int>&nums, int start)
    {
        if(start>= nums.size()) return;

        for(int i =start; i<nums.size(); i++)
        {
            if(i>start&&nums[i] == nums[i-1])//这里要对同一层遍历去重,所以是i>start
            {
                continue;
            }
            path.push_back(nums[i]);
            res.push_back(path);
            backtracking(nums, i+1);
            path.pop_back();

        }
    }

public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        res.clear();
        path.clear();
        res.push_back(path);
        backtracking(nums, 0);
        return res;

    }
};

递增子序列

[题]

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
1 <= nums.length <= 15
-100 <= nums[i] <= 100

[例]

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

[思路]

因为含有重复元素,所有必须对同一层重复元素去重。另外要求子集长度大于2且递增。

[代码]
class Solution {
    vector<vector<int>> res;
    vector<int> path;

    void trackbacking( vector<int> & nums, int start)
    {
        if(path.size() >= 2)    res.push_back(path);
        int used[201] = {0};
        for( int i = start; i< nums.size(); ++i)
        {
            if ((!path.empty() && nums[i] < path.back())//
                    || used[nums[i] + 100] == 1) {
                    continue;
            }
            used[nums[i] + 100] = 1; 
            path.push_back(nums[i]);
            trackbacking(nums, i+1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> findSubsequences(vector<int>& nums) {
        trackbacking(nums, 0);
        return res;
    }
};

总结:

组合问题:

  1. 数组没有重复元素,且一个元素只能用一次。 组合 组合总和Ⅲ
    for 循环从startindex开始, 递归startindex+1。
  2. 数组没有重复元素,但是可以重复使用同一个元素。 组合总和Ⅰ
    for 循环从startindex开始, 递归startindex。
  3. 数组有重复元素,且一个元素只能用一次。(树层去重)组合总和Ⅱ
    for 循环从startindex开始, 递归startindex。
    if (i > start && candidates[i] == candidates[i - 1]) {//同一层相同元素直接跳过
  4. 求组合的个数。 组合总和Ⅳ
    记忆化搜索树 / 动态规划(此类问题在另一篇博客动态规划会详细介绍)

全排列问题:

  1. 数组没有重复元素。(树枝去重)全排列
    for 循环从startindex开始
    用一个hash数组标记树枝中已使用过的元素
  2. 数组有重复元素。(树枝去重+树层去重)全排列Ⅱ
    for 循环从startindex开始
    用一个hash数组标记树枝中已使用过的元素
    if(i>0 && nums[i-1] == nums[i] && map[i-1]==0) continue;//同一层去重,这里跟组合中的去重不一样

分割问题:

分割回文串
复原ip地址

子集问题

子集
子集Ⅱ
递增子序列

补充:

组合问题的变体:

电话号码的字母组合

[题]

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述

[例]

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

`[思路]

本题其实是一个组合问题。不含有重复元素且只能使用一次的组合。
关键在于建立一个从输入数字到字母的映射。
递归一次就使用下一个数字对于的字符串。
然后在for循环中遍历这个字符。

[代码]
class Solution {
private:
    const string letterMap[10] = {//建立数字到字符的映射
        "", // 0
        "", // 1
        "abc", // 2
        "def", // 3
        "ghi", // 4
        "jkl", // 5
        "mno", // 6
        "pqrs", // 7
        "tuv", // 8
        "wxyz", // 9
    };
public:
    vector<string> result;
    string s;
    void backtracking(const string& digits, int index) {
        if (index == digits.size()) {
            result.push_back(s);
            return;
        }
        int digit = digits[index] - '0';        // 将index指向的数字转为int
        string letters = letterMap[digit];      // 取数字对应的字符集
        for (int i = 0; i < letters.size(); i++) {//遍历这个字符集
            s.push_back(letters[i]);            //保存
            backtracking(digits, index + 1);    // 递归,注意index+1,一下层要处理下一个数字了
            s.pop_back();                       // 回溯
        }
    }
    vector<string> letterCombinations(string digits) {
        s.clear();
        result.clear();
        if (digits.size() == 0) {
            return result;
        }
        backtracking(digits, 0);
        return result;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值