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

一、电话号码的字母组合

题目解析

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

相关推荐
AI视觉网奇1 小时前
blender 导入fbx 黑色骨骼
学习·算法·ue5·blender
weixin_468466852 小时前
目标识别精度指标与IoU及置信度关系辨析
人工智能·深度学习·算法·yolo·图像识别·目标识别·调参
多恩Stone2 小时前
【3D AICG 系列-8】PartUV 流程图详解
人工智能·算法·3d·aigc·流程图
铸人2 小时前
再论自然数全加和-质数的规律
数学·算法·数论·复数
历程里程碑3 小时前
Linux22 文件系统
linux·运维·c语言·开发语言·数据结构·c++·算法
你撅嘴真丑10 小时前
第九章-数字三角形
算法
uesowys10 小时前
Apache Spark算法开发指导-One-vs-Rest classifier
人工智能·算法·spark
ValhallaCoder10 小时前
hot100-二叉树I
数据结构·python·算法·二叉树
董董灿是个攻城狮10 小时前
AI 视觉连载1:像素
算法
智驱力人工智能11 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算