算法——LeetCode22. 括号生成

22. 括号生成

原题链接

题目:

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

 

示例:

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

2. 题解

2.1 解法1 : dfs

递归状态记录: 当前括号path, 左括号数量, 右括号数量, 以及 n

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> ans = new ArrayList<>();
            int lc = 0, rc = 0;
            dfs(ans, "", n, lc, rc);
            return ans;
        }

        public void dfs(List<String> ans, String path, int n, int lc, int rc) {
            if (rc > lc || lc > n || rc > n) return;
            if (lc == rc && lc == n) {
                ans.add(path);
                return;
            }
            dfs(ans, path + '(', n, lc + 1, rc);
            dfs(ans, path + ')', n, lc, rc + 1);
        }
    }

参考: 入门级别的回溯法,学会套路,秒懂十题!

2.2 题解2:暴力递归法(通过递归生成所有括号组合,然后判断其是否有效)

主要流程:

  1. 编写判断括号组合是否有效的函数,主要方法为
  • 遍历该组合序列,使用balance记录左括号减去右括号的数量,
  • 若遍历过程中balance<0或遍历结束时balance不为0,则序列无效
  1. 编写递归函数。主要流程如下:
  • 先判断深度是否达到n,若达到,则判断该组合序列是否有效,有效加入结果集,返回
  • 否则深度为达到n,继续向下生成,添加左右括号

代码:

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> res = new ArrayList<>();
            combination(res, n, 0, "");
            return res;
        }

        //递归函数:参数分别为,结果集,最大深度,深度,当前结点字符串
        public void combination(List<String> res, int n, int index, String s) {
            if (index == 2 * n) {//注意这里为2n,因为是括号组合,所以应该是两倍长度
                if (isValid(s)) {
                    res.add(s);
                }
                return;
            } else {
                combination(res, n, index + 1, s + "(");
                combination(res, n, index + 1, s + ")");
            }
        }

        //有效判断函数
        public boolean isValid(String s) {
            int balance = 0;
            for (int i = 0; i < s.length(); i++) {
                if (balance < 0) {
                    return false;
                }
                if (s.charAt(i) == '(') {
                    balance++;
                } else if (s.charAt(i) == ')') {
                    balance--;
                }
            }
            return balance == 0;
        }

    }

2.3 题解3:回溯法

主要思路:

  • 基于解法一的改进,加入剪枝,递归调用前判断,当左括号数量小于n,右括号数量小于左括号数量时才执行递归

代码:

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<String> res = new ArrayList<>();
            combination(res, n, 0, 0, "");
            return res;
        }

        //递归函数,参数分别为,结果集,括号数量,当前结点左括号数量,右括号数量,字符串
        public void combination(List<String> res, int n, int left, int right, String s) {
            if (s.length() == 2 * n) {//注意这里为2n,因为是括号组合,所以应该是两倍长度
                if (isValid(s)) {
                    res.add(s);
                }
                return;
            } else {
                if (left < n) {
                    combination(res, n, left + 1, right, s + "(");
                }
                if (left > right) {
                    combination(res, n, left, right + 1, s + ")");
                }
            }
        }

        //有效判断函数
        public boolean isValid(String s) {
            int balance = 0;
            for (int i = 0; i < s.length(); i++) {
                if (balance < 0) {
                    return false;
                }
                if (s.charAt(i) == '(') {
                    balance++;
                } else if (s.charAt(i) == ')') {
                    balance--;
                }
            }
            return balance == 0;
        }

    }

2.4 题解4:动态规划

主要思路:

  1. 设置状态,dp[i],表示括号对数为i时的所有有效括号列表(结果集)
  2. 现在由dp[i-1]递推dp[i]的结果
  3. 下一个结果,开头的元素必定是左括号,然后需要找到与之匹配的右括号,而其所匹配的右括号存在的位置可能在末尾也可能在中间,所以可以得到递推公式如下:
  • dp[i]="("+dp[可能的括号组合]+")"+dp[剩下的括号组合],且 可能的括号组合+剩下的括号组合=i-1,
  1. 设 可能括号组合为 j,剩下括号组合为i-j-1 (j=0,1...,i-1) 那么递推公式可写为:
  • dp[i]="("+dp[j]+")"+dp[i-j-1]; (j=0,1...,i-1)

代码:

    class Solution {
        public List<String> generateParenthesis(int n) {
            List<List<String>> dp = new ArrayList<>();
            //初始化dp0
            List<String> dp0 = new ArrayList<>();
            dp0.add("");
            dp.add(dp0);
            //循环递推
            for (int i = 1; i <= n; i++) {
                //下面推出dp[i]
                List<String> temp = new ArrayList<>();//存储dp[i]下的所有组合
                for (int j = 0; j < i; j++) {//设置可能括号组合
                    //将dp[j]中所有的括号组合拿出来推
                    for (String a : dp.get(j)) {
                        for (String b : dp.get(i - j - 1)) {
                            temp.add("(" + a + ")" + b);//得到一种组合
                        }
                    }
                }
                dp.add(temp);//最终将得到的列表赋值给dp[i]
            }
            return dp.get(n);
        }

    }

参考: 回溯算法(深度优先遍历)+ 广度优先遍历(Java)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值