我爱学算法之—— 递归回溯综合(一)

一、电话号码的字母组合

题目解析

用过9键打字的小伙伴应该非常理解这道题的大致含义;

数字2-9分别对应一些字符串,例如2对应abc3对应def;当按下数字23时,可能出现的字母组合就有ad、ae、af、bd、be、bf、cd、ce、cf等。

这道题给定应该字符串(其中只包含2-9的数组),要求出所有可能出现的字母组合。

算法思路

这道题整体思路还是非常简单的,根据给定的字符串,依次选择字符即可

代码实现

cpp 复制代码
class Solution {
    string path;
    vector<string> ret;
    const vector<string> str = {"",    "",    "abc",  "def", "ghi",
                                "jkl", "mno", "pqrs", "tuv", "wxyz"};
    void dfs(const string& digits, int pos) {
        if (pos == digits.size()) {
            ret.push_back(path);
            return;
        }
        int num = digits[pos] - '0';
        for (int i = 0; i < str[num].size(); i++) {
            path.push_back(str[num][i]);
            dfs(digits, pos + 1);
            path.pop_back();
        }
    }
public:
    vector<string> letterCombinations(string digits) 
    {
        dfs(digits,0);
        return ret;
    }
};

二、括号生成

题目解析

这道题给定一个n,我们要找出所有可能的并且有效的n组括号的组合。(括号有效:)前一定有(与其一一对应------)

算法思路

对于这道题,整体思路:

递归找出 n对括号(2*n个字符),所有的排列情况,然后找出有效的括号组合即可。

找出所有的排列组合情况,再判断有效太麻烦了;就需要在找所有排列组合情况时,进行判断剪枝操作。

剪枝

有效的括号组合 : 任意(之前都有)和它一一对应,任意(之后都有)与它一一对应

)之前有(与之对应 : 当前)之前,(的数量一定大于等于)的数量。cnt_left >= cnt_right(算上该(

(之后有)与之对应 : 在最终的排列组合中,()的数量相等即可。cnt_left == cnt_right(在)前一定有(与之对应的基础上)

代码实现

cpp 复制代码
class Solution {
    vector<string> ret; // 结果
    string path;
    int cnt_left = 0;  // path 中 ( 数量
    int cnt_right = 0; // path 中 ) 数量
public:
    void dfs(int n, int pos) {
        if (pos == 2 * n) {
            if (cnt_left == cnt_right)
                ret.push_back(path);
            return;
        }
        // (
        cnt_left++;
        path.push_back('(');
        dfs(n, pos + 1);
        // 回溯
        cnt_left--;
        path.pop_back();
        if (cnt_left <= cnt_right)
            return; // 剪枝
        cnt_right++;
        path.push_back(')');
        dfs(n, pos + 1);
        // 回溯
        cnt_right--;
        path.pop_back();
    }
    vector<string> generateParenthesis(int n) {
        dfs(n, 0);
        return ret;
    }
};

三、组合

题目解析

给定两个整数 nk,要求出[1,n]范围中所有可能的k个数的组合

算法思路

对于这道题,可以说丝毫没有难度;(两层for循环遍历,即可)

这里递归实现,dfs的功能就是,找出[begin,n]中所有可能的k个数的组合。

这里之所以是[begin , n],主要是为了去重(例如[2,3][3,2]);

这样选择一个数x之后,在[x,n]中选择其他的数,完成去重操作。

path,当前选择的数组合的个数等于k时,统计结果即可。

代码实现

cpp 复制代码
class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    bool vis[22] = {false};
public:
    void dfs(int begin, int n, int k) {
        if (path.size() == k) {
            ret.push_back(path);
            return;
        }
        for (int i = begin; i <= n; i++) {
            if (vis[i])
                continue;
            path.push_back(i);
            vis[i] = true;
            // 递归
            dfs(i+1, n, k);
            // 回溯
            path.pop_back();
            vis[i] = false;
        }
    }
    vector<vector<int>> combine(int n, int k) {
        dfs(1, n, k);
        return ret;
    }
};

四、目标和

题目解析

给定一个非负整数数组nums和一个整数target,要求给每一个nums中的元素加上符号(+-),找出最终和为tarhet的不同表达式的数目。

算法思路

给一个元素加符号,就是+或者-

这里递归遍历数组nums时为每个元素枚举 +- 两种符号选择,通过递归深入处理后续元素。递归至数组末尾时,校验当前累加和是否等于目标值,符合则计数加 1。每完成一个符号分支的递归后,回溯恢复累加和状态,最终统计所有有效符号组合的总数。

代码实现

cpp 复制代码
class Solution {
    int ret = 0;
    int sum = 0;
public:
    void dfs(vector<int>& nums, int target, int pos) {
        if (pos == nums.size()) {
            if (sum == target)
                ret++;
            return;
        }
        // +
        sum += nums[pos];
        dfs(nums, target, pos + 1);
        // 回溯
        sum -= nums[pos];
        // -
        sum -= nums[pos];
        dfs(nums, target, pos + 1);
        // 回溯
        sum += nums[pos];
    }
    int findTargetSumWays(vector<int>& nums, int target) {
        dfs(nums, target, 0);
        return ret;
    }
};

五、组合总和

题目解析

这道题给定一个candidates数组,其中所有元素互不相同,且2 <= candidates[i] <=40;以及一个目标整数target

candidates中任选元素(可以重复),要求和为target;最终返回所有的不同组合。

算法思路

在上述的递归回溯题目中,都是选择指定数量个元素;而这道题要求我们选择和为target,所以递归结束的判断条件就是和>= target因为candidates数组中的所有元素都是大于1

去重

最终要返回所有不同的组合,[2,3,3][3,2,3][3,3,2]这些可以认为都是相同的组合。

这里在递归函数中,就从pos位置之后的选择元素([pos,n]

代码实现

cpp 复制代码
class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    int sum = 0;
public:
    void dfs(vector<int>& candidates,int target,int pos)
    {
        if(sum == target)   ret.push_back(path);
        if(sum >= target)   return;
        for(int i = pos; i< candidates.size();i++)
        {
            // 选当前位置
            sum += candidates[i];
            path.push_back(candidates[i]);
            dfs(candidates,target,i);
            // 回溯
            sum -= candidates[i];
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(candidates,target,0);
        return ret;
    }
};

本篇文章到这里就结束了,感谢支持

我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2oul0hvapjsws

相关推荐
m0_736919102 小时前
C++中的策略模式实战
开发语言·c++·算法
孞㐑¥2 小时前
算法—位运算
c++·经验分享·笔记·算法
软件算法开发2 小时前
基于卷尾猴优化的LSTM深度学习网络模型(CSA-LSTM)的一维时间序列预测算法matlab仿真
深度学习·算法·matlab·lstm·一维时间序列预测·卷尾猴优化·csa-lstm
高洁012 小时前
知识图谱如何在制造业实际落地应用
深度学习·算法·机器学习·数据挖掘·知识图谱
BHXDML2 小时前
数据结构:(二)逻辑之门——栈与队列
java·数据结构·算法
晚风吹长发2 小时前
初步了解Linux中的信号捕捉
linux·运维·服务器·c++·算法·进程·x信号
机器学习之心2 小时前
MATLAB基于GA-ELM与NSGA-Ⅱ算法的42CrMo表面激光熔覆参数多目标优化
算法·matlab·ga-elm
TracyCoder1232 小时前
LeetCode Hot100(17/100)——240. 搜索二维矩阵 II
算法·leetcode
FJW0208142 小时前
haproxy的调度算法
算法