LeetCode - 22 括号生成【中等】

22. 括号生成

难度 中等

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

示例:

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

广度优先遍历

借助队列的方式从1到n扩增结果集,每次对上一轮的所有括号组合 post 做如下操作:

  • 外包一层括号
  • 左边添加一个括号
  • 右边添加一个括号

失败,扩增规则不能囊括所有有效结果,且存在重复项

补充:可以通过队列的方式模拟DFS思路

class Solution {
public:
    //使用广度优先搜索时,需要结点自身维护信息,因此定义一个结点结构体
    struct Node {
        string str;
        int left, right;
        Node(string _str, int _left, int _right):str(_str), left(_left), right(_right) {}
    };

    vector<string> generateParenthesis(int n) {
        vector<string> ans;
        if (n == 0) return ans;

        queue<Node> q;
        q.push(Node("", n, n));
        //每轮从队列中取出有效括号串的结点,尝试添加括号
        while (!q.empty()) {
            Node current = q.front(); q.pop();
            //结点已经生成为有效结果,加入结果集
            if (current.left == 0 && current.right == 0) ans.push_back(current.str);
            if (current.left > 0)
                q.push(Node(current.str + "(", current.left - 1, current.right));
            if (current.right > 0 && current.left < current.right)
                q.push(Node(current.str + ")", current.left, current.right - 1));
        }

        return ans;
    }
};

深度优先遍历

从空串开始利用递归逐步构建有效的括号串,最终加入结果集

当括号对数为 n 时,转化为用于构建括号串的剩余左括号还剩 n 个,右括号还剩 n 个。每向下递归一层时,使用一个左括号或者右括号,加入当前括号串中,相应括号数量减少一个。当左右括号都使用完时,即构建了一个可能的结果

递归过程中需要剪枝排除无效的括号串——当前剩余可使用的括号中,右括号剩余的数量严格大于左括号剩余的数量,否则意味着构成的括号串中存在不成对的括号,括号串无效

class Solution {
public:
    vector<string> ans;
    vector<string> generateParenthesis(int n) {
        if (n == 0) return ans;

        DFS("", n, n);
        
        return ans;
    }
    //当前递归结果,剩余可用的左括号,剩余可用的右括号
    void DFS(string current, int left, int right) {
        //使用完所有括号的有效结果添加到结果集中
        if (left == 0 && right == 0) {
            ans.push_back(current);
            return;
        }
        //剪枝,去除违反生成规则的不合理的括号串
        //剩余可用左括号严格大于有括号,说明当前符号串存在未完成闭合的右括号,生成括号串非法
        if (left > right) return;
        
        //从左向右添加左括号和右括号,试图生成有效括号串
        if (left > 0) DFS(current + "(", left - 1, right);
        if (right > 0) DFS(current + ")", left, right - 1);
    }
};

(注:C++中向函数传递的参数默认是拷贝,应声明为传引用才能达到修改原对象的目的)

动态规划

该题所求的括号序列大致如下:

()
(())  ()()
((()))  (()())  (())()  ()(())  ()()()
...

对于 i = n 和 i = n - 1 两种情况, n 的可行解相比 n - 1 的可行解多出了一对新的括号

假设 i = n 的可行解中,最左端的左括号和其对应的右括号当做相比于 i = n - 1 时增加的括号,那么剩余部分的括号可能分布的位置有:

  1. 新增括号的内部,将这部分记为 P
  2. 新增括号的右侧,将这部分记为 Q

由此 i = n 时括号串的表达式可以记为:

"(" P ")" Q

将规则应用于实际的括号组合中:

//i = 0 的空串和 i = 1 的单个括号视为已知
//i = 2
(())    // P = "()", Q = ""
()()    // P = "", Q = "()"
//i = 3
((()))    // P = "(())", Q = ""
(()())    // P = "()()", Q = ""
(())()    // P = "()", Q = "()"
()(())    // P = "", Q = "(())"
()()()    // P = "", Q = "()()"

进一步我们可以发现,P和Q代表着 i = p , i = q 时构成的括号串的结果集,而 P 和 Q 的数目又满足 p + q = n - 1

由此我们可以使用动态规划求解括号串的结果集

/*
**	0.定义状态:dp[i]表示 括号数目为i时的结果集
**	1.状态转移方程:dp[i] = {"(" + dp[p] + ")" + dp[q]};
**	3.初始化:dp[0] = {""}, dp[1] = {"()"};
**	4.输出
**	5.空间优化
*/
class Solution {
public:
	vector<string> generateParenthesis(int n) {
		if (n == 0) return {};
		if (n == 1) return { "()" };
        //开辟dp数组
		vector<vector<string>> dp(n+1);
		dp[0] = { "" };
		dp[1] = { "()" };
        //从n = 2开始填写dp数组
		for (int i = 2; i <= n; i++) {
			for (int j = 0; j <i; j++) {    //p从0到i-1遍历,q从i-1到0遍历
                //取出结果集中所有括号串进行组合
				for (string p : dp[j])
					for (string q : dp[i - j - 1]) {
						string str = "(" + p + ")" + q;
						dp[i].push_back(str);
					}
			}
		}
		return dp[n];
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值