【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++)

这篇具有很好参考价值的文章主要介绍了【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 前言

后面的练习是接着下面链接中的文章所继续的,在对后面的题练习之前,可以先将下面的的文章进行了解👇:

【算法】{画决策树 + dfs + 递归 + 回溯 + 剪枝} 解决排列、子集问题(C++)

2. 算法题

22.括号生成

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树


思路

  • 题意分析:要求根据给出的数字,算出合法的括号组成个数。根据题目,我们可以总结出下面的规则:
    【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树
  • 解法dfs + 根据决策树设计递归、回溯、剪枝
  • 决策树
    【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树
    • 根据上图决策树,即可直接着手编写代码:
    • 细节问题
      【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

代码文章来源地址https://www.toymoban.com/news/detail-835610.html

class Solution {
public:
    int left, right, _n; // 分别记录左右括号的数量
    vector<string> ret; // 结果数字
    string path;
    vector<string> generateParenthesis(int n) {
        _n = n;
        dfs();
        return ret;
    }

    void dfs()
    {
        if(right == _n) // 当前path序列有效,加入结果
        {
            ret.push_back(path);
            return;
        }

        if(left < _n) // 添加左括号
        {
            path.push_back('('); left++;
            dfs();
            path.pop_back(); left--;
        }

        if(right < left) // 添加右括号
        {
            path.push_back(')'); right++;
            dfs();
            path.pop_back(); right--;
        }
    }
};

494.目标和

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

思路

  • 题意分析:对于数组给出的数字,通过向所有数字前补加减号,使最后的值为 target
  • 解法dfs + 根据决策树设计递归、回溯、剪枝
    • 该解法并非最优解法,但这里依然用dfs做,并引出一些写法问题。
    • 决策树:(省略了后面的步骤)

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树
- 根据决策树的思路,不难看出实际上与 题目 78.子集 非常相似,代码也是如此,但有一个需要注意的细节,先看代码:

代码1

class Solution {
public:
    int ret, path, _target;
    
    int findTargetSumWays(vector<int>& nums, int target) {
        _target = target;
        dfs(nums, 0); // 从0位置开始
        return ret;
    }

    void dfs(vector<int>& nums, int pos)
    {
        // 终止条件
        if(pos == nums.size())
        {
            if(path == _target) ret++;
            return;
        }

        // 正号
        path += nums[pos];
        dfs(nums, pos + 1);
        path -= nums[pos];

        // 负号
        path -= nums[pos];
        dfs(nums, pos + 1);
        path += nums[pos];
    }
};
  1. 最开始我们提到,深搜dfs并非该题的最优解法, 时间开销很大,对于上面的代码,更是面临超时的风险,上面的代码将 path作为全局变量
  2. 我们可以 进行优化:将path作为参数传递
  3. 更改后的代码,时间开销会小一些

代码2

class Solution {
public:
    int ret, _target;
    
    int findTargetSumWays(vector<int>& nums, int target) {
        _target = target;
        dfs(nums, 0, 0); // 从0位置开始
        return ret;
    }

    void dfs(vector<int>& nums, int pos, int path)
    {
        // 终止条件
        if(pos == nums.size())
        {
            if(path == _target) ret++;
            return;
        }

        // 正号
        dfs(nums, pos + 1, path + nums[pos]);

        // 负号
        dfs(nums, pos + 1, path - nums[pos]);
    }
};

path定义形式的理由

  1. 频繁的执行 + - 操作,是一比不小的时间消耗,直接传参可以只需要将计算后的结果直接作为参数递归
  2. 而对于 [78.子集],我们将path定义为全局变量,因为对于该题情况,path 本身为vector类型,作为参数传递会导致频繁的创建vector,不利于节省时间。

39.组合总和

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

思路

  • 题意分析:即通过给定的数组任意分配,组成和为目标值的序列,求序列的种类。
  • 解法dfs + 根据决策树设计递归、回溯、剪枝

解法1

  • 决策树:如图所示:
    • 即每次分支都进行所有数字的选择,符合条件的继续,由于题目要去重,同层下选过的不再复选
    • 剪枝:同层的选过的剪掉 + 该路径和超出目标值或该位置超出数组范围的剪掉
      【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

代码

class Solution {
public:
    int _target;
    vector<int> path;
    vector<vector<int>> ret;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        _target = target;
        dfs(candidates, 0, 0);
        return ret;
    }

    void dfs(vector<int>& nums, int pos, int pathSum)
    {
        // 路径和等于目标值,记录结果,向上返回
        if(pathSum == _target)
        {
            ret.push_back(path);
            return;
        }
        // 路径和大于目标值 / 当前位置超出数组; 向上返回
        if(pathSum > _target || pos >= nums.size()) return;

        for(int i = pos; i < nums.size(); ++i)
        {
            path.push_back(nums[i]);
            dfs(nums, i, pathSum + nums[i]);
            path.pop_back();
        }
    }
};

解法2

  • 决策树
    【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

对于上图,有一个需要注意的细节问题:
- 对于恢复现场:

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

此时我们可以编写代码:

代码

class Solution {
public:
    int _target;
    vector<int> path;
    vector<vector<int>> ret;

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        _target = target;
        dfs(candidates, 0, 0);
        return ret;
    }

    void dfs(vector<int>& nums, int pos, int pathSum)
    {
        // 路径和等于目标值,记录结果,向上返回
        if(pathSum == _target)
        {
            ret.push_back(path);
            return;
        }
        // 路径和大于目标值 / 当前位置超出数组; 向上返回
        if(pathSum > _target || pos >= nums.size()) return;

        for(int k = 0; k * nums[pos] <= _target; ++k)
        {
            if(k) path.push_back(nums[pos]); // 枚举 当前值的个数,添加到数组中
            dfs(nums, pos + 1, pathSum + k*nums[pos]);
        }

        // 恢复现场
        for(int k = 1; k * nums[pos] <= _target; ++k)
        {
            // 将每个元素添加过的个数值 都恢复
            path.pop_back();
        }
    }
};

784.字母大小写全排列

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

思路

  • 题意分析:即返回所有转换字母大小写后的字符串
  • 决策树:如下图所示
    • 当当前位置是字母时,进行变与不变的分支,当是数字时,直接继续,无分支,最后叶子节点处即为结果。
      【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

代码

class Solution {
public:
    string path; // 记录当前字母序列
    vector<string> ret;

    vector<string> letterCasePermutation(string s) {
        dfs(s, 0);
        return ret;
    }

    void dfs(string s, int pos)
    {
        if(path.size() == s.size())
        {
            ret.push_back(path);
            return;
        }

        char ch = s[pos];
        // 不改变的情况:数字 以及 字母的第一种情况
        path += ch;
        dfs(s, pos + 1);
        path.pop_back();

        if(isalpha(ch)) // 需要改变  
        {
            // 改变大小写
            if(ch <= 'z' && ch >= 'a') ch -= 32;
            else ch += 32;

            path += ch;
            dfs(s, pos + 1);
            path.pop_back();
        }
    }
};

526. 优美的排列

【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++),算法,算法,剪枝,深度优先,c++,决策树

思路

  • 题意分析:即找出所有的符合规则(优美排列)的排列
  • 解法利用全排列 与 规则
    • 决策树:这里不再画决策树了,相当于全排列的思路
    • 全排列的思路即:如果该位置未被检索过,则加入path中并继续dfs
    • 这里我们增加一条判定,即当前位置的下标与perm[i]满足整除关系时,才进行dfs

代码

class Solution {
public:
    bool used[16];
    int ret;

    int countArrangement(int n) {
        dfs(1, n); // 从下标为1处填n个数
        return ret;
    }

    void dfs(int pos, int n)
    {
        if(pos == n + 1){ // 更新结果
            ret += 1;
            return;
        }

        // 全排列 + 判断是否符合优美排列
        for(int i = 1; i <= n; ++i)
        {
            if(!used[i] && (i % pos == 0 || pos % i == 0))
            {
                used[i] = true;
                dfs(pos + 1, n);
                used[i] = false; // 恢复现场
            }   
            
        }
    }
};

到了这里,关于【算法】递归、回溯、剪枝、dfs 算法题练习(组合、排列、总和问题;C++)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • DFS:深搜+回溯+剪枝解决排列、子集问题

                                        创作不易,感谢三连支持!!  . - 力扣(LeetCode) . - 力扣(LeetCode)  方案1:不合法就continue 方案2:合法才能进循环 . - 力扣(LeetCode) . - 力扣(LeetCode)  策略1:决策树以选不选作为参考,结果为叶子节点 策略2:决策树以选几个

    2024年04月16日
    浏览(61)
  • 【算法】回溯:与递归,dfs的同质与分别,剪枝与恢复现场的详细理解,n皇后的回溯解法及算法复杂度分析。

    目录 ​编辑 1.什么是回溯 2.关于剪枝 3.关于恢复现场 4.题目:二叉树的所有路径(凸显恢复现场:切实感受回溯与深搜) 问题分析 ①函数设置为:void Dfs(root) ②函数设置为:void Dfs(root,path) 解题思想:使⽤深度优先遍历(DFS)求解。 代码实现 5.N后问题 问题分析 4皇后的放置

    2024年04月16日
    浏览(37)
  • LeetCode刷题13:回溯+剪枝解决216.组合总和 III

    找出所有相加之和为  n   的  k   个数的组合,且满足下列条件: 只使用数字1到9 每个数字  最多使用一次   返回  所有可能的有效组合的列表  。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 示例 1: 输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他

    2024年02月02日
    浏览(41)
  • 【C/C++练习】经典的排列组合问题(回溯算法)——电话号码的字母组合

    📖题目描述 题目出处 :电话号码的字母组合 示例: 📖题解  这是一道典型的排列组合问题,根据输入,我们需要找到所有的组合。下面以输入字符串 digits = \\\"23\\\" 为例来讲解这道题目。 图解: 分析:  首先要知道输入的字符串 \\\"23\\\" 中的数字字符分别对应哪些字符串,其中

    2024年02月16日
    浏览(35)
  • 组合(力扣)dfs + 回溯 + 剪枝 JAVA

    给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 你可以按 任何顺序 返回答案。 示例 1: 输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 示例 2: 输入:n = 1, k = 1 输出:[[1]] 提示: 1 = n = 20 1 = k = n 解题思路: 1.每个元素有选与不选两种情况,

    2024年02月16日
    浏览(47)
  • DFS:深搜+回溯+剪枝解决组合问题

                                                   创作不易,感谢支持!!! . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) . - 力扣(LeetCode) 该题和前面是类似的,但是用回溯算法,会超

    2024年04月12日
    浏览(32)
  • 递归实现 组合问题+排列问题(DFS)

    目录 递归实现排列型枚举 递归实现排列类型枚举 II  递归实现组合型枚举 递归实现组合型枚举 II 递归实现指数型枚举 递归实现指数型枚举 II 递归不是循环,递归利用了系统栈,只要是函数都会被系统管理。当执行到函数地址入口时就会为函数在系统栈上分配一块内存。当

    2024年02月15日
    浏览(39)
  • LeetCode算法题解(回溯)|39. 组合总和、40. 组合总和 II、131. 分割回文串

    题目链接:39. 组合总和 题目描述: 给你一个  无重复元素  的整数数组  candidates  和一个目标整数  target  ,找出  candidates  中可以使数字和为目标数  target  的 所有   不同组合  ,并以列表形式返回。你可以按  任意顺序  返回这些组合。 candidates  中的  同一个  数

    2024年02月05日
    浏览(56)
  • leetcode216. 组合总和 III(回溯算法-java)

    来源:力扣(LeetCode) 链接:https://leetcode.cn/problems/combination-sum-iii 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件: 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。 示例 1: 输

    2024年02月10日
    浏览(42)
  • 【Leetcode60天带刷】day27回溯算法——39. 组合总和,40.组合总和II,131.分割回文串

    ​ 39. 组合总和 给你一个  无重复元素  的整数数组  candidates  和一个目标整数  target  ,找出  candidates  中可以使数字和为目标数  target  的 所有   不同组合  ,并以列表形式返回。你可以按  任意顺序  返回这些组合。 candidates  中的  同一个  数字可以  无限制重复

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包