1.题目
2.题目说明
给定一个数字n,n表示生成括号数量,比如题中n=3,表示生成3对括号。括号可以相交或者互不相交或者部分相交。输出所有的情况并以列表的形式返回。
注:输出情况必须是有序的,不然报错~像我用python的话,不放心可以在得到结果后面加个sort
3.各路解法
解法一
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
if n == 0:
return [""]
elif n == 1:
return ["()"]
elif n == 2:
return ["()()","(())"]
result = []
for i in range(n):
temp1 = self.generateParenthesis(i)
temp2 = self.generateParenthesis(n-i-1)
result.extend(["(%s)%s" % (p,q) for p in temp1 for q in temp2])
return result
思路:dfs~
n=0,1,2
作为初始状态,然后对0~n-1
的情况分别递归i
和n-i-1
。但是这里要注意,实际上递归一共生成i+n-i-1=n-1
个括号,所以倒数第二行在构造结果的时候,多加了一个括号。
不减那个1行不行,也不行。因为当n=3,i=0
的时候,n-i=3
,会陷入无限递归。
extend() 函数用于在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。
解法二
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
ans = []
def backtrack(S, left, right):
if len(S) == 2*n:
ans.append(''.join(S))
return
if left < n:
S.append('(')
backtrack(S, left+1, right)
S.pop()
if right < left:
S.append(')')
backtrack(S, left, right+1)
S.pop()
backtrack([], 0, 0)
return ans
思路:回溯~
用一个列表储存当前内容,left表示左括号的个数,right表示右括号的个数。第一个if
表示如果长度达到了2n(n对括号总长度是2n),就把这个加入到结果中。第二个if
表示如果左括号数量小于n,就可以加左括号。第三个if
表示如果右括号数量小于左括号,则可以加右括号。
S.pop()
表示把加的括号去掉,这就是回溯。因为每当开始一个新的递归,我可以选择左括号+1或者右括号+1,对应的就是两种不同的情况,我可以先尝试左括号+1,尝试完了,就左括号-1,即S.pop()
。然后再判断是否满足right<left
,再给右括号+1,然后再回溯。通过将每种情况都试一遍,行就记录然后回退,不行就直接回退,从而得到所有解~
另外,这样的if
语句安排,能够保证输出的顺序没有问题。
解法三
class Solution(object):
def generateParenthesis(self, n):
if n == 1:
return ['()']
results = set([])
for i in range(1, n):
l1 = self.generateParenthesis(i)
l2 = self.generateParenthesis(n - i)
for item1 in l1:
for item2 in l2:
results.add(item1[:-1] + item2 + ')')
results.add(item1 + item2)
return list(results)
思路:dfs~
这个dfs和第一种各有所长吧。这个只考虑了n=1的情况,n=0的时候,其实并不需要考虑,因为两个列表分别是0+n
和1+(n-1)
的情况其实是一样的。
最后那个嵌套for循环里面为啥要加两个。可以简单的这样理解:如果n=1,就一个括号;如果n=2,就是两个括号嵌套或者并列。results.add(item1[:-1] + item2 + ')')
不就是两个括号嵌套吗。results.add(item1 + item2)
不就是两个括号并列吗。如果n>3,那不就拆分成若干个1和2的加和,就完事了。
解法四
class Solution:
def generateParenthesis(self, n: int) -> List[str]:
dp=[]
dp.append([''])
for i in range(1,n+1):
temp=[]
for j in range(i):
temp+=['('+l+')'+r for l in dp[j] for r in dp[i-1-j]]
dp.append(temp)
return dp[-1]
思路:dp~
dp明确两点,初始状态和状态转移方程。
初始状态:['']
转移方程:dp[i] = '(' + dp[j] + ')' + dp[i-1-j]
temp用来记录当前内容。转移方程必须是j
和i-j-1
,不减一就会变成dp[i] = dp[j] + dp[i-j]
,这样没法求dp[i]
。另外,为什么括号加在dp[j]
外面,看了解法三咱就知道,主要考虑的就是嵌套和并列的情况。因为这里j
是可以取0的,取0的时候加个括号就是并列。不取0的时候加个括号就是嵌套了。不理解的同学可以自己拿草稿纸出来画一下~
3.总结
总的来说,还是dp速度快一点,内存占用基本都是13.7MB左右。先画图,然后再实现,有错就多调试,问题不大~