LeetCode:22. Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
假设有n对括号,求出所有形式正确的组合。例如:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路一:动态规划
由于括号肯定是成对出现的,所以第0位置肯定是"(",而这个左括号对应的")"肯定只会出现在第1,3,5…位置,因为一对括号之间要么不包含别的括号,要么也是包含成对的括号,所以之间的间隔肯定是偶数。
我们先用3对括号举例子,设i对括号的正确组合结果为f(i),那么f1="()",f2=[f1+"()","("+f1+")","()"+f1],f3=[f1+"(())","(("+f1+"))","(())"+f1,f2+"()","("+f2+")","()"+f2]。注意这里的f3,它有三种情况,一种是f1和两对括号的组合,还有一种是f2和一对括号的组合,最后一种情况是f1和f2的组合。1,2两种情况只是fj的位置摆放不同,可以合并。第3种情况是单纯由前面的结果组合而成。
所以i对括号的结果公示可以写成如下:
f ( i ) = { ′ ( ) ′ i = 1 ∑ j = 1 n − 1 ( [ f j + ( n − j ) ′ ( ′ + ( n − j ) ′ ) ′ ) , [ ( n − j ) ′ ( ′ + f j + ( n − j ) ′ ) ′ ) , [ ( n − j ) ′ ( ′ + ( n − j ) ′ ) ′ + f j ) , ] ) ∑ k = 1 n − 1 ( f k + f i − k ) f(i)= \begin{cases} '()' & i=1\\ \sum_{j=1}^{n-1}([fj+(n-j)'('+(n-j)')'),[(n-j)'('+fj+(n-j)')'),[(n-j)'('+(n-j)')'+fj),]) \\ \sum_{k=1}^{n-1}(f_k+f_{i-k})& \end{cases} f(i)=⎩⎪⎨⎪⎧′()′∑j=1n−1([fj+(n−j)′(′+(n−j)′)′),[(n−j)′(′+fj+(n−j)′)′),[(n−j)′(′+(n−j)′)′+fj),])∑k=1n−1(fk+fi−k)i=1
l = 0
allAns = []
class Solution:
def realGenerateParenthesis(self, n: int) -> List[str]:
global allAns
left = "("
right = ")"
allAns[0]=[left+right]
if n == 1:
return allAns[0]
for i in range(1,n):
print(i) # i+1对括号
curSet = set()
ans = []
for j in range(1,i+1):
# 情况1,2.由不同数量的左右括号和f(j)组成
thisleft = left*j
thisright = right*j
lastAns = allAns[i-j]
for last in lastAns:
tmp=last+thisleft+thisright
curSet.add(tmp)
tmp=thisleft+last+thisright
curSet.add(tmp)
tmp=thisleft+thisright+last
curSet.add(tmp)
# 情况3,单纯由两个f(j)组成
leftPart = allAns[j-1]
rightPart = allAns[i-j]
for l in leftPart:
for r in rightPart:
curSet.add(l+r)
for c in curSet:
ans.append(c)
# 缓存起来,供下一步使用
allAns[i]=(ans)
return allAns[len(allAns)-1]
def generateParenthesis(self, n: int) -> List[str]:
global allAns
allAns = [[] for i in range(n)]
res = self.realGenerateParenthesis(n)
return res
思路二:回溯法
这个思路就是在添加(左/右)括号的过程中,通过个数来判断括号是否合法。
# Backtracking
class Solution(object):
def generateParenthesis(self, N):
ans = []
def backtrack(S = '', left = 0, right = 0):
if len(S) == 2 * N:
ans.append(S)
return
if left < N:
backtrack(S+'(', left+1, right)
if right < left:
backtrack(S+')', left, right+1)
backtrack()
return ans
left
表示已经添加的左括号的个数,right
表示已经添加的右括号的个数。值得注意的是由于左括号肯定是优先插入,所以先判断 left < N
。当S的长度达到2N的时候表示全部插入完毕。可以用下面的树状图表示插入过程,以N=3为例:
('',0,0)
|
('(',1,0)
/ \
('((',2,0) ('()',1,1)
/ \
('(()',2,1) ('()(',2,1)
/ \
('(())',2,2) ('()()',2,2)
复杂度分析
实际上对于N,符合情况的括号对排列的个数是一个卡特兰数:
C n = 1 n + 1 ( 2 n n ) C_n=\frac{1}{n+1}(\begin{array}{lr} 2n\\ n\\ \end{array} ) Cn=n+11(2nn)
然后又因为 C n ∼ 4 n n n C_n \sim \frac{4^n}{n\sqrt{n}} Cn∼nn4n,所以这种方法的时间复杂度就是 O ( 4 n n ) O(\frac{4^n}{\sqrt{n}}) O(n4n)
思路三:Closure Number
官方答案还提供了这样一种思路,其实主要思想我们在思路一中也提到了。就是位置0肯定是左括号,然后对应的右括号肯定只能出现在1,3,5…这样的奇数位置。一对括号之间可能会包含0到多个括号对,同样它的右侧也可能会出现0到多个括号对。抽象写法就是'({}){}'.format(left, right)
。这种方法就是思路一的简写形式。
Python 代码实现
class Solution(object):
def generateParenthesis(self, N):
if N == 0: return ['']
ans = []
for c in xrange(N):
for left in self.generateParenthesis(c):
for right in self.generateParenthesis(N-1-c):
ans.append('({}){}'.format(left, right))
return ans
THE END.