如何返回n对圆括号的所有有效组合?

时间:2013-12-12 06:30:16

标签: python recursion backtracking

def paren(n):
    lst = ['(' for x in range(n)]
    current_string = ''.join(lst)
    solutions = list()
    for i in range(len(current_string)+1):
        close(current_string, n, i, solutions)
    return solutions

def close(current_string, num_close_parens, index, solutions):
    """close parentheses recursively"""
    if num_close_parens == 0:
        if current_string not in solutions:
            solutions.append(current_string)
        return
    new_str = current_string[:index] + ')' + current_string[index:]
    if num_close_parens and is_valid(new_str[:index+1]):
        return close(new_str, num_close_parens-1, index+1, solutions)
    else:
        return close(current_string, num_close_parens, index+1, solutions)

def is_valid(part):
    """True if number of open parens >= number of close parens in given part"""
    count_open = 0
    count_close = 0
    for paren in part:
        if paren == '(':
            count_open += 1
        else:
            count_close += 1
    if count_open >= count_close:
        return True
    else:
        return False

print paren(3)

以上代码是我尝试解决上述问题。它为n<3提供了足够的解决方案,但除此之外,它没有给出所有解决方案。例如,当n=3时,它会输出['()()()', '(())()', '((()))']而忽略'()(())'。如何修改代码以正确输出所有可能的解决方案?

7 个答案:

答案 0 :(得分:7)

这是一个递归生成器,可以生成所有有效的解决方案。与其他答案不同,此答案从不计算需要过滤的重复或无效字符串。这与this answer to a previous question中的算法几乎相同,但它不需要非递归的辅助函数:

def paren(left, right=None):
    if right is None:
        right = left  # allows calls with one argument

    if left == right == 0: # base case
        yield ""

    else:
        if left > 0:
            for p in paren(left-1, right): # first recursion
                yield "("+p

        if right > left:
            for p in paren(left, right-1): # second recursion
                yield ")"+p

答案 1 :(得分:6)

如果不必使用递归,这似乎有效:

from itertools import permutations

def valid(test):
  open, close = 0, 0
  for c in test:
    if c == '(':
      open += 1
    elif c == ')':
      close += 1
      if close > open:
        return False
  return True

def paren(n):
  perms = set(''.join(p) for p in permutations('(' * n + ')' * n))
  return [s for s in perms if valid(s)]

答案 2 :(得分:1)

使用递归和效率高于itertools.permutations

def paren(n):
    ps = set(['(' * n + ')' * n])
    for i in range(1, n):
        for a in paren(i):
            for b in paren(n-i):
                ps.add(a + b)
    return ps

由于重复递归,使用functools.lru_cache也可以提高效率。

或者,通过内置的memoization,

def paren(n, known={}):
    if n in known:
        return known[n]
    ps = set(['(' * n + ')' * n])
    for i in range(1, n):
        for f in paren(i, known):
            for s in paren(n-i, known):
                ps.add(f + s)
    known[n] = ps
    return ps

答案 3 :(得分:1)

我是动态编程和递归的新手,但这是我没有递归的解决方案。请告诉我为什么它不起作用或者这是否是可接受的解决方案:

class Parenthesis(object):
    def __init__(self, parens):
        self.parens = parens
        self.my_valid_parens = {
                                1: ['()'],
                                2: ['()()', '(())']
                               }

    def generate_valid_paren(self):
        if self.parens <= 2:
            return self.my_valid_parens[self.parens] 

        i = 3
        while i <= self.parens:
            new_set = []
            for each in self.my_valid_parens[i-1]:
                new_set += set([each + '()', '()' + each, '(' + each + ')'])
            self.my_valid_parens[i] = list(new_set)
            i += 1

if __name__ == '__main__':
    num = 4
    p = Parenthesis(num)
    p.generate_valid_paren()
    print p.my_valid_parens[num]

以下是分别为num = 3和4时的输出:

3: ['(()())', '()()()', '()(())', '(())()', '((()))']

4: ['(()())()', '()(()())', '((()()))', '()()()()', '(()()())', '()()(())', '(()(()))', '()(())()', '((())())', '(())()()', '()(())()', '()((()))', '(((())))', '((()))()']

答案 4 :(得分:0)

似乎任务归结为生成具有N + 1个节点的所有可能树。让我们假设在整个字符串周围有另一对parens,然后对于N = 3,所有可能的树将是

  o
  |
  o         ((()))
  |
  o
  |
  o

  o
 / \        (())()
o   o
|
o

  o
 / \
o   o       ()(())
    |
    o

  o         ()()()
 /|\
o o o

我现在无法为您提供任何代码(因此CW),但请参阅this paper - 它似乎完全处理了这个问题。

答案 5 :(得分:0)

对于N == 3,有5种有效组合:()()(),((())),(()()),(())()和()(())。

递归算法的工作原理如下:

  • 通过向左添加一个来从左到右构建有效字符串 或者一次一个右括号。
  • 基本情况:使用了所有左括号和右括号(左== n&amp;&amp;对== n)。只需返回一个空字符串。否则:
  • 如果没有使用所有这些,请打印左括号 (左&lt; n),并用(n,left + 1,right)
  • 调用子问题
  • 仅在使用权限数量时打印右括号 括号小于左括号的数量(右&lt; 剩下)。使用(n,left,right + 1)
  • 调用子问题

这是Java代码:

public static ArrayList<String> parentheses(int n, int left, int right) {

ArrayList<String> results = new ArrayList<String>();

  if (left == n && right == n) {
    results.add("");
  }

  if (left < n) {
    ArrayList<String> subResults = parentheses(n, left + 1, right);
    for (String subResult : subResults) {
      String newResult = "(" + subResult;
      results.add(newResult);
    }
  }

  if (left > right) {
    ArrayList<String> oldResults = parentheses(n, left, right + 1);
    for (String oldResult : oldResults) {
      String newResult = ")" + oldResult;
      results.add(newResult);
    }
  }

  return results;
}

最后,使用:

调用递归函数
parentheses(n, 0, 0);

答案 6 :(得分:0)

这是我的解决方案

from itertools import permutations
n = 3
input = ("( " * n) + (") " * n)
in_list = [f for f in input.split(" ") if f]
possible_combinations = list(permutations(in_list, n*2))
valid_list = []

def ret_valid(el):
    num_open = num_close = 0
    for val in el:
        if val == "(":
            num_open += 1
        else:
            num_close += 1
        if num_close > num_open:
            return False
    return True

for el in possible_combinations:
    if ret_valid(el):
        if "".join(el) not in valid_list:
            valid_list.append("".join(el))
print(", ".join(valid_list))