生成括号递归作为列表vs字符串传递

时间:2019-11-27 02:45:00

标签: python python-3.x recursion

我正在用leetcode解决生成括号问题。如果我使用以下代码(将其作为要添加的列表):

这里f是前括号的计数器,b是后括号的计数器。

def generateParenthesis(n: int) -> List[str]:

    def recursion(f, b, cur=[]):

        if len(cur)==(n*2):
            a ="".join(cur)
            output.append(a)
            return
        if f:
            cur.append("(")
            recursion(f-1,b, cur)
        if b:
            cur.append(")")
            recursion(f,b-1, cur)
    output = []
    f = n
    b = n
    recursion(f, b, cur)

    return output

运行上述解决方案时,我得到的输出与n = 3一样:

  

[“(((()))”]

如果不是将其追加到列表中,而是将cur作为字符串,并在其上加上“(”),即可得到正确的答案。

def generateParenthesis(n: int) -> List[str]:

    def recursion(f, b, cur=""):

        if b < f or b<0 or f<0:
            return

        if len(cur)==n*2:
            output.append(cur)
            return
        if f:
            recursion(f-1,b, cur+"(")
        if b:
            recursion(f,b-1, cur+")")


    output = []
    f = n
    b = n
    recursion(f, b)

    return output

这将产生当前输出,即:

  

[“(((()))”,“(()())”,“(())()”,“()(())”,“()()()”] < / p>

我知道字符串是不可变的,所以在字符串上加“(”就是O(N)。我只是想避免这种情况。任何人都可以帮助我了解这里发生的事情以及是否/如何使用其中的第一个代码正确的方法。我认为这与在每次递归中使用单独的列表(名为cur的变量)有关。

2 个答案:

答案 0 :(得分:1)

首先,我不喜欢-> List[str]:recursion(f, b, cur)的语法,因为在代码中未定义List,在该区域中也未定义cur。

现在让我们在第一部分中查看您的代码。

def generateParenthesis(n: int) -> List[str]:

    def recursion(f, b, cur=[]):

        if len(cur)==(n*2):
            a ="".join(cur)
            output.append(a)
            return
        if f:
            cur.append("(")
            recursion(f-1,b, cur)
        if b:
            cur.append(")")
            recursion(f,b-1, cur)
    output = []
    f = n
    b = n
    recursion(f, b, cur)

    return output

通过引用将变量cur传递到递归中。这意味着,所有被调用的递归都共享相同的cur变量。因此,在执行过程中,一旦生成"((()))",其他递归仍将增加cur的长度,从而使"((()))"是唯一的输出。所以如果你。将条件更改为if len(cur) >= n*2,您将获得以下输出。

  

['(((()))','(((())))','(((()))))','(((())))))']

要使代码正常工作,一种幼稚的方法是每次调用递归时都创建一个新列表。例如,recursion(f-1, b, deepcopy(cur)),其中deepcopyfrom copy import deepcopy。但我相信在这种情况下,您宁愿使用不可变的字符串

另一方面,您也可以将列表作为堆栈查看。在每个递归调用中,都将附加一个括号,这表示您在堆栈中一个括号。因此,您还应该在递归调用之后弹出插入的括号。尽管在这种情况下,应使用您以前的检查条件。

由于Python的list与C ++的vector类似,因此我们只需维护最后一个元素的索引即可,而不是实际弹出。修改后的代码应为

def generateParenthesis(n):

    def recursion(f, b, cur=[None] * (2*n), idx= 0):

        if b < f or b<0 or f<0:
            return

        if idx >= (n*2):
            a ="".join(cur)
            output.append(a)
            return
        if f:
            cur[idx] = "("
            recursion(f-1,b, cur, idx + 1)
        if b:
            cur[idx] = ")"
            recursion(f,b-1, cur, idx + 1)

    output = []
    f = n
    b = n
    recursion(f, b)

    return output

让我们做print(generateParenthesis(3)),我们得到

  

['(((()))','(()())','(())()','()(())','()()()'] < / p>

好。

PS:Python没有本地linkedlist,但有本地dequehttps://docs.python.org/3/library/collections.html#collections.deque),当需要更复杂的推入和弹出操作时,可以使用此数据结构。

答案 1 :(得分:1)

  

我知道字符串是不可变的,因此在字符串上加上“(”是O(N)。   只是想避免这种情况。

但是有趣的是,您的字符串解决方案比@Tony基于列表的可变解决方案要快25%!至少以我的时间generateParenthesis(13)

下面是我基于字符串的解决方案-比@Tony慢15%左右,但没有副作用,并且可以转换为列表(甚至更慢)无危险默认值的基于解决方案的解决方案。即您可以将内部递归函数移至其自己的顶级函数,并且仍然可以使用:

def parenthesis(n):

    def parenthesis_recursive(k, f, b, current=""):
        if not b >= f >= 0 <= b:
            return []

        if len(current) == k:
            return [current]

        return parenthesis_recursive(k, f - 1, b, current + "(") + parenthesis_recursive(k, f, b - 1, current + ")")

    return parenthesis_recursive(n * 2, n, n)

print(parenthesis(3))