leetcode22题 括号生成 dfs和动态规划

leetcode22题 括号生成

题目

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

有效括号组合需满足:左括号必须以正确的顺序闭合。

示例 1:

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

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

提示:

1 <= n <= 8

解法一(DFS)

这道题可以描述成一颗二叉树,然后通筛选掉不合适的结果,返回正确的结果,先看当括号数量为2时的图例

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3s1GUpkn-1637659055398)(E:\typro图片保存\leetcode第22题深度(1)].png)

通过观察图,我们能够得出以下结论:

  1. 如果我们想继续产生孩子(无论是左孩子还是右孩子),前提是左右括号的数量没有用完,比如左括号已经用了俩个了,那么接下来就不能继续产生左括号了
  2. 产生左括号很简单,只需要看左括号的数量还够不够,但是在产生右括号的时候就不同了,他首先要看数量,其次还要看左边括号的数量是不是已经等于已经存在的右括号的数量了,如果相等,那么你在加上一个右括号,那他一定孤寡了
  3. 至于结果那里,就是左右括号都用光了,就结束了

代码

class Solution {
    //保存结果的集合
    List<String> result = new ArrayList<>();
    public List<String> generateParenthesis(int n) {
        dfs("",0,0,n,result);
        return result;
    }
    //str代表存储临时结果的字符串,left和right就代表左右括号已经存在的数量,result:结果集合
    public void dfs(String str,int left,int right,int n,List<String> result){
        //当左右括号用尽,结果自然就出来了,把临时结果str塞进集合
        if(left == n && right == n) {
            result.add(str);
            return;
        }
        //一旦左边的括号数量比右边的括号数量少了,那肯定不匹配了,直接放弃这个
        if(left < right){
            return;
        }
        //递归这,看上面的图和条件就能理解了
        if(left < n){
            dfs(str + "(",left + 1,right,n,result);
        }
        if(right < n){
            dfs(str + ")",left,right + 1,n,result);
        }
    }
}

解法二(动态规划)

这道题的题解看了很长时间才看懂,但是叙述起来还是有些问题,就随便记录一下了。

例如现在我们想求解三对括号可能的所有组合,那么按照动态规划的理解,我们必须要知道一对括号和俩对括号的时候的所有的组合结果,那么现在假设知道了这个结果,对我们求解三对括号的情况下有哪些作用呢。

现在我们先抛开上面的问题,拿一对括号举例,我们只能得到一种结果,也就是(),那现在弱国我们在一对的基础上想得到俩对括号的所有结果应该怎么办呢,通过观察发现我们要么是把一对括号的结果放在新加入的括号的内部,要么放在外部,也就是俩种结果(())、()(),

接下来我们回归第一个问题中的三对括号的所有情况,我们同样先把新加入的括号单拿出来,对于剩下的俩对括号还是别无选择,要么在这对新括号的里面要么在外面,但是这还是不够的,还有可能的结果是一对在里面、一对在外面,在看俩对都在里面或是外面的情况,也不相同,因为我们在分析俩对括号的时候得出了俩个不同的解,所以都需要一一考虑,因此我们可以这样进行考虑假设p=在新增括号里面的括号对数、q=在新增括号外卖呢的括号对数、f§ =里面的括号对数的所有组合、f(q) = 外面的括号对数的所有组合。而很显然,我们只需要将f§中的所有情况和f(q)中所有的情况组合在一起,就能得到里面括号对数为p、外面括号对数为q的情况下所有的括号组合,但是这显然只是其中一种,因为p和q的值需要改变,比如在括号对数为5的情况下,p、q一一对应的状态应该是[0,1,2,3,4] [4,3,2,1,0],因此我们只需要在将p和q所有状态列举出来,在将该状态下所有的结果列举出来,也就等到了最终结果。

代码

class Solution {
//-----------------------------动态规划----------------------------------
    public List<String> generateParenthesis(int n) {
        //这里先给初始状态因为p和q的状态有可能为空,所以给了俩个初始值,然后用dp去记录不同对数的括号的情况下产生的结果
        List<List <String>> dp = new ArrayList<>();
        List <String> l0 = new ArrayList<>();
        l0.add("");
        List <String> l1 = new ArrayList<>();
        l1.add("()");
        dp.add(0,l0);
        dp.add(1,l1);
        for(int i = 2;i <= n;i++){
            //记录当前结果
            List<String> current = new ArrayList<>();
            for(int j = 0;j < i;j++){
                //获取括号对数为p的时候的所有组合
                List<String> inside = dp.get(j);
                //获取括号对数为q的时候所有的组合
                List<String> outSide = dp.get(i - j - 1);
                //将拿到的结果遍历组合
                for(String in : inside){
                    for(String out : outSide){
                        String curResult = "(" + in + ")" + out;
                        current.add(curResult);
                    }
                }
            }
            dp.add(current);
        }
        return dp.get(n);
    }

}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值