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

一、电话号码的字母组合

题目解析

用过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

相关推荐
大鱼>19 分钟前
宠物行为识别AI算法详解:从数据采集到模型部署的完整方案
人工智能·深度学习·算法·宠物
王莎莎-MinerU24 分钟前
解析回归集:RAG 和 Agent 上线前,先把表格、公式、版面测清楚
人工智能·算法·数据挖掘·回归·pdf·ocr
KaMeidebaby29 分钟前
卡梅德生物技术快报|兔单克隆抗体:噬菌体 Fab 免疫文库搭建实操:兔单克隆抗体重组构建完整参数
前端·网络·数据库·人工智能·算法
AndrewHZ30 分钟前
【LLM技术全景】混合精度与分布式训练:训练大模型的工程奥秘
人工智能·分布式·深度学习·算法·ai·语言模型·llm
AI科技星2 小时前
基于全域数学公理体系求解三元约束极值题【乖乖数学】
人工智能·算法·机器学习·密码学·拓扑学·乖乖数学·全域数学
伊玛目的门徒9 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
Jerry9 小时前
LeetCode 226. 翻转二叉树
算法
想做小南娘,发现自己是女生喵11 小时前
【无标题】
数据结构·算法
Kx_Triumphs12 小时前
HDU4348 To the moon(主席树区间修改模板)
算法·题解