LeetCode 22. 括号生成 Generate Parentheses

博客围绕给定 n 代表生成括号的对数,要求生成所有可能且有效的括号组合展开。介绍了三种方法,包括暴力法,生成所有序列并检查有效性;回溯法,在序列保持有效时添加括号;闭合数法,将序列表示为不相交子集总和来枚举,还给出了各方法的复杂度分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

 

方法一:暴力法

思路

我们可以生成所有 22n2^{2n}2​2n​​ 个 '('')' 字符构成的序列。然后,我们将检查每一个是否有效。

算法

为了生成所有序列,我们使用递归。长度为 n 的序列就是 '(' 加上所有长度为 n-1 的序列,以及 ')' 加上所有长度为 n-1 的序列。

为了检查序列是否为有效的,我们会跟踪平衡,也就是左括号的数量减去右括号的数量的净值。如果这个值始终小于零或者不以零结束,该序列就是无效的,否则它是有效的。

复杂度分析

  • 时间复杂度:O(22nn)O(2^{2n}n)O(2​2n​​n),对于 22n2^{2n}2​2n​​ 个序列中的每一个,我们用于建立和验证该序列的复杂度为 O(n)O(n)O(n)。

  • 空间复杂度:O(22nn)O(2^{2n}n)O(2​2n​​n),简单地,每个序列都视作是有效的。请参见方法三以获得更严格的渐近界限。
     


方法二:回溯法

思路和算法

只有在我们知道序列仍然保持有效时才添加 '(' or ')',而不是像方法一那样每次添加。我们可以通过跟踪到目前为止放置的左括号和右括号的数目来做到这一点,

如果我们还剩一个位置,我们可以开始放一个左括号。 如果它不超过左括号的数量,我们可以放一个右括号。

复杂度分析

我们的复杂度分析依赖于理解 generateParenthesis(n) 中有多少个元素。这个分析超出了本文的范畴,但事实证明这是第 n 个卡塔兰数 1n+1(2nn)\dfrac{1}{n+1}\binom{2n}{n}​n+1​​1​​(​n​2n​​),这是由 4nnn\dfrac{4^n}{n\sqrt{n}}​n√​n​​​​​4​n​​​​ 渐近界定的。

  • 时间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}})O(​√​n​​​​​4​n​​​​),在回溯过程中,每个有效序列最多需要 n 步。

  • 空间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}})O(​√​n​​​​​4​n​​​​),如上所述,并使用 O(n)O(n)O(n) 的空间来存储序列。
     


方法三:闭合数

思路

为了枚举某些内容,我们通常希望将其表示为更容易计算的不相交子集的总和。

考虑有效括号序列 S闭包数:至少存在index> = 0,使得 S[0], S[1], ..., S[2*index+1]是有效的。 显然,每个括号序列都有一个唯一的闭包号。 我们可以尝试单独列举它们。

算法

对于每个闭合数 c,我们知道起始和结束括号必定位于索引 02*c + 1。然后两者间的 2*c 个元素一定是有效序列,其余元素一定是有效序列。

复杂度分析

  • 时间和空间复杂度:O(4nn)O(\dfrac{4^n}{\sqrt{n}})O(​√​n​​​​​4​n​​​​),该分析与方法二类似。

 

public class Solution {
    public static void main(String[] args) {
		System.out.println(generateParenthesis(3));
	}
	
	public static ArrayList<String> generateParenthesis(int n) {
		ArrayList<String> list = new ArrayList<String>();
		rec(n, 0, 0, "", list);
		return list;
    }
	
	public static void rec(int n, int left, int right, String s, ArrayList<String> list){
		// invariant必须满足左括号数目要大等于右括号数目
		if(left < right){
			return;
		}
		
		// 如果左右括号数目相等则添加到list
		if(left==n && right==n){
			list.add(s);
			return;
		}
		
		// 左括号已满,只能添加右括号
		if(left == n){
			rec(n, left, right+1, s+")", list);
			return;
		}
		
		rec(n, left+1, right, s+"(", list);		// 继续添加左括号
		rec(n, left, right+1, s+")", list);		// 继续添加右括号
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值