题目
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路
这道题我用递归来做,直觉上是设置两个flag,分别叫做needright和remainleft,因为括号有左右必须成对的语法,所以needright用来记录已有多少个孤独的左括号没有和右括号成对,即现在需要多少个右括号,remainleft是说目前还有多少个空余的左括号没有还可以用,因为可以(((这样直接叠加而不造成语法错误。对于remainleft=0和needright=0的情况就是下一个必须添加左括号和右括号。其他的情况就是下面两行,只要先加左后加右就行了。
代码
class Solution(object):
def generateParenthesis(self, n):
"""
:type n: int
:rtype: List[str]
"""
self.result = []
self.needright = 1
self.remainleft = n-1
self.gp(n, self.needright, self.remainleft,'', '(')
return self.result
def gp(self, n, needright, remainleft, succeed, parth):
if len(succeed) == 2 * n:
self.result.append(succeed)
#print('reach')
return
succeed += parth
if remainleft == 0:
self.gp(n, needright-1, remainleft, succeed, ')')
elif needright == 0:
self.gp(n, 1, remainleft-1, succeed, '(')
else:
self.gp(n, needright+1, remainleft-1, succeed, '(')
self.gp(n, needright-1, remainleft, succeed,')')
注意
值得一提的是,这道题让我注意到了python的参数传递方式和对象的不同。
赋值
Python中,一切都是对象,赋值也都是引用。如a=123
这个简单的赋值,事实上是解释器在内存中创建了一个对象123,然后再创建了一个对象是个变量a,a里面存放的是123在内存里的地址。
a = 123
b = a
a = '321'
这小段代码实际上是让b也指向了内存中的123。
python值传递还是引用传递
上面说了,python中的赋值是都是引用,但问题在于赋值的变量是什么对象,python中的对象分为可变对象(list,dict,set等)和不可变对象(number, string, tuple等)。
- 可变对象
可变对象,该对象所指向的内存中的值可以被改变。变量(准确的说是引用)改变后,实际上是其所指的值直接发生改变,并没有发生复制行为,也没有开辟新的出地址,通俗点说就是原地改变。 - 不可变对象
不可变对象,该对象所指向的内存中的值不能被改变。当改变某个变量时候,由于其所指的值不能被改变,相当于把原来的值复制一份后再改变,这会开辟一个新的地址,变量再指向这个新的地址。
对于我这道题的代码来说比如说递归了三层以后needright = 2 remainleft = 1,然后继续递归完成一个result.append,之后又返回到这一层,由于needreight和remainleft都是number,属于不可变对象,所以返回来之后就还是2,1,但如果有个变量比如succeed现在不是字符串了,把他设置成一个列表,因为列表是可变对象,如果在后面的递归中对列表进行了改变,那么即使返回到这一层,succeed还是会被改变,不再是刚进入这一层时的succeed了。
下面说一下值传递和引用传递,按照C++的概念
- 值传递
对于函数输入的参数对象,函数执行中首先生成对象的一个副本,并在执行过程中对副本进行操作。执行结束后对象不发生改变。 - 引用传递
对于函数输入的参数对象,函数在执行过程中对对象本身进行操作;在执行结束后参数对象有可能发生改变函数的值
结合起来看,如果对象是可变对象,那么进行引用传递,如果对象是不可变对象,那么就值传递。