当前位置: 首页 > news >正文

[Lc4_dfs] 括号生成 | 组合 | 目标和

目录

1.括号生成

题解

2.组合

3.目标和

题解


1.括号生成

链接:22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:

输入:n = 3
输出:["((()))","(()())","(())()","()(())","()()()"]

示例 2:

输入:n = 1
输出:["()"]

题解

给几对括号,然后把括号组合一下形成 有效括号

首先我们要知道什么是 有效括号的组合

  • 1.左括号的数量 = 右括号的数量
  • 2.从头开始的任意一个子串,左括号数量 >= 右括号数量

对于这样 暴力枚举的所有情况的问题,我们还是画一颗决策树

把所有情况不重不漏的情况都画出来,然后根据这棵树我们写代码。

每个位置都有两种选择,但是注意到刚开始就有剪枝的情况,刚开始不能选右括号,因为不满足条件2,所以右括号我们要分情况剪枝。

  • 当右括号数量大于等于左括号的数量时此时不能添加右括号 right >= left。
  • 还有当左括号的数量大于等于n时此时就没有左括号可以选了,left >= n。
  • 后面情况都是这样分析的,因此我们就可以做写代码的准备了。

全局变量

  • 需要一个 left 记录左括号的数量
  • right 记录右括号的数量
  • 还有一个n记录有几对括号要组合。
  • 还需要一个ret记录结果,path记录每条路径的结果。

递归函数

  • 每个位置都有两种选择。
  • 因为上面都是用的全局变量,因此递归函数参数什么都不用传了。dfs()。回溯 当把path放到ret里,返回后要恢复现场。
  • pop掉path最后一个位置元素。
  • 剪枝 前面已经分析了。

递归出口 当right==n的时候说明括号组合完了。

class Solution {
    // dfs 就代表 向下走一步的意思
public:
    int left = 0;
    int right = 0;
    int _n;
    vector<string> ret;
    string path;

    vector<string> generateParenthesis(int n) {
        _n = n;
        dfs();
        return ret;
    }

    void dfs() {
        if (right == _n) {
            ret.push_back(path);
            return;
        }

        if (left < _n) // 通过 设计条件 进行剪枝
        {
            path += '(';
            left++;
            dfs();
            // 回溯
            path.pop_back();
            left--;
        }

        if (right < left) {
            path += ')';
            right++;
            dfs();
            path.pop_back();
            right--;
        }
    }
};

2.组合

链接:77. 组合

给定两个整数 nk,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

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

注意这道题结果是不能重复的。

1,2 和 2,1 虽然位置不同但是是同样的组合。

这样的题我们还是画出决策树,然后根据这棵树把所有细节分析清楚

  • 每个位置都有4种选择,首先前面被选种的数字后面就不能再选了,如1,2 和2,1只是位置不同数都是一样的,因此还是重复。
  • 其次上每一层都是从上一层被选数字的后面一个开始选的。

  • 全局变量,一个ret记录最终结果,一个path记录每条路径的结果。
  • 递归函数,给一个pos,每一层都从pos位置开始往后选,dfs(pos)
  • 回溯 当递归结束往上返回要恢复现场pop掉path最后一个位置元素
  • 剪枝 用pos控制开始的位置就是剪枝
  • 递归出口 当path.size() == k 就可以把path放到ret里,然后结束本次递归。
class Solution {
public:
    vector<vector<int>> ret;
    vector<int> path;
    int _n, _k;

    vector<vector<int>> combine(int n, int k) {
        _n = n;
        _k = k;
        dfs(1);
        return ret;
    }

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

        for (int i = pos; i <= _n; i++) { //走的这一步是什么情况
            path.push_back(i);
            dfs(i + 1); //向下走一步
            path.pop_back();
        }
    }
};

3.目标和

链接:494. 目标和

给你一个非负整数数组 nums 和一个整数 target

向数组中的每个整数前添加 '+''-' ,然后串联起所有整数,可以构造一个 表达式

  • 例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1"

返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。

示例 1:

输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3

示例 2:

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

题解

给一个数组,对数组每个数字前面添加 + 或者 - ,串联所有数字构成一个表达式

找出表达式和为目标值有多少种情况。

  • 如果前面做过选子集的问题,这道题思想是一模一样的,找子集其中有一张方法是 这个数字 选or不选
  • 这道题就是 这个数字前面 +or-。

我们就根据这个画出决策树。

这里的步骤都不说了,和子集哪里的一模一样。

最后到叶子节点这条路径的和加起来等于target,就是一种情况。

这里写代码有两种形式。

  • path 是全局变量的时候的代码
  • path 作为参数的代码

可以参考求二叉树所有路径那道题,这道题也是path作为全局变量和作为参数两种不同形式的代码。

path作为全局变量, 需要考虑回溯恢复现场。

  • path作为参数,不用考虑,因为在递归在向上返回的时候就已经帮助我们回溯恢复现场了。
  • path作为参数也是有回溯的不过是编译器就可以帮我回溯了。

如果path是一个单独的类型的话,如int类型,你会发现把它放在dfs参数里面写的话,代码比较简洁。

如果path是一个数组类型的话,推荐使用全局变量!

这里 path 是 int,我们可以都先写着 对比来看看

path作为全局变量的代码

class Solution {
public:
    int ret;
    long long path;
    int _target;

    int findTargetSumWays(vector<int>& nums, int target)
    {
        _target=target;
        dfs(nums,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]; 
      }
};

path 是 int 作为参数

class Solution {
public:
    int ret;

    int findTargetSumWays(vector<int>& nums, int target) 
    {
        dfs(nums,target,0,0);
        return ret;
    }

    void dfs(vector<int>& nums,int target,int pos,int path)
    {
        if(pos==nums.size())
        {
            if(path==target)
                ret++;
            return;
        }

        dfs(nums,target,pos+1,path+nums[pos]);//不要 修改原数
        dfs(nums,target,pos+1,path-nums[pos]);
    }
};


导图总结

相关文章:

  • docker创建registry镜像仓库2.8版本
  • 宝塔面板部署 Laravel 项目无法访问静态资源的解决方法
  • MySQL 进阶语法:函数、约束、多表查询、事务
  • 分支结构- P1424-小鱼的航程-第二十六天
  • 从dev分支checkout出一个functionA分支开发功能
  • SvelteKit 最新中文文档教程(11)—— 部署 Netlify 和 Vercel
  • 树状数组 3 :区间修改,区间查询
  • K8S学习之基础五十一:k8s部署jenkins
  • Thera图像超分辨率模型使用
  • openpnp,cadence SPB17.4,placement - 从allegro中导出坐标文件的选项会影响贴片精度
  • 3ds Max 2026 新功能全面解析
  • 每日算法-250326
  • 23种设计模式-组合(Composite)设计模式
  • 汇编(六)——汇编语言程序格式及MASM
  • Checksum方法实现
  • C#基础学习(五)函数中的ref和out
  • VSCode 市场发现恶意扩展正在传播勒索软件!
  • kettle插件-rabbitmq插件
  • 23种设计模式-访问者(Visitor)设计模式
  • 无参数读文件和RCE
  • 科普|治疗腰椎间盘突出症,筋骨平衡理论如何提供新视角?
  • 在海拔3980米驻守:“全国先进工作者”刘鹏与洛戈梁子警务站的9年
  • 外交部亚洲司司长刘劲松就日本民用飞机侵闯我钓鱼岛领空向日方提出严正交涉
  • 浙商银行一季度净赚超59亿微增0.61%,非息净收入降逾22%
  • 金砖国家外长会晤发表主席声明,强调南方国家合作
  • 上海“模速空间”:将形成人工智能“北斗七星”和群星态势