括号的有效排列

时间:2010-07-03 17:06:32

标签: algorithm

  

可能重复:
  Solution to a recursive problem (code kata)

给出一个算法来查找给定n的括号的所有有效排列 例如:

for n=3, O/P should be
{}{}{} 
{{{}}}
{{}}{} 
{}{{}} 
{{}{}}

4 个答案:

答案 0 :(得分:106)

问题概述

这是一个经典的组合问题,它以许多不同的方式表现出来。这些问题基本相同:

  • 生成所有可能的方法来平衡N对括号(即此问题)
  • 生成将二元运算符应用于N+1因子
  • 的所有可能方法
  • 使用N+1
  • 生成所有完整二叉树
  • 许多其他人......

另见


一种简单的递归解决方案

这是一个简单的递归算法,用于解决Java中的这个问题:

public class Parenthesis {
    static void brackets(int openStock, int closeStock, String s) {
        if (openStock == 0 && closeStock == 0) {
            System.out.println(s);
        }
        if (openStock > 0) {
            brackets(openStock-1, closeStock+1, s + "<");
        }
        if (closeStock > 0) {
            brackets(openStock, closeStock-1, s + ">");
        }
    }
    public static void main(String[] args) {
        brackets(3, 0, "");
    }
}

以上打印(as seen on ideone.com):

<<<>>>
<<><>>
<<>><>
<><<>>
<><><>

Recursion Tree for n=3

基本上我们会跟踪有多少打开和关闭的括号“库存”供我们使用,因为我们正在递归地构建字符串。

  • 如果库存中没有任何内容,则完全构建字符串,您可以将其打印出来
  • 如果库存中有可用的左括号,请尝试将其添加。
    • 现在你有一个不太开放的括号,但一个更接近的括号来平衡它
  • 如果库存中有一个紧密的括号,请尝试将其添加。
    • 现在你有一个较少的括号

请注意,如果您在尝试添加左括号之前交换递归的顺序,以便尝试添加一个紧密括号,则只需获得相同的平衡括号列表,但顺序相反! (see on ideone.com)。


“优化”变体

上述解决方案非常简单明了,但可以进一步优化。

最重要的优化是在字符串构建方面。虽然它看起来像表面上的简单字符串连接,但上面的解决方案实际上有一个“隐藏的”O(N^2)字符串构建组件(因为将一个字符连接到长度为String的不可变NO(N)操作)。一般来说,我们通过使用可变StringBuilder来优化它,但对于这种特殊情况,我们也可以简单地使用固定大小的char[]index变量。

我们还可以通过简化递归树进行优化。我们不是像在原始解决方案中那样递归“双向”,而是可以只递归“单向”,并以迭代方式执行“其他方式”。

在下文中,我们使用char[]index代替String进行了两种优化,并且仅递归添加开括号,迭代添加紧密括号:{{3 }}

public class Parenthesis2 {
    public static void main(String[] args) {
        brackets(4);
    }
    static void brackets(final int N) {
        brackets(N, 0, 0, new char[N * 2]);
    }
    static void brackets(int openStock, int closeStock, int index, char[] arr) {
        while (closeStock >= 0) {
            if (openStock > 0) {
                arr[index] = '<';
                brackets(openStock-1, closeStock+1, index+1, arr);
            }
            if (closeStock-- > 0) {
                arr[index++] = '>';
                if (index == arr.length) {
                    System.out.println(arr);
                }
            }
        }
    }
}

递归逻辑现在不太明显,但这两种优化技术是有益的。


相关问题

答案 1 :(得分:6)

虽然不是一个真正的算法,但一个好的起点是加泰罗尼亚数字:

参考

答案 2 :(得分:2)

Eric Lippert最近在他的文章Every Tree There Is中发表了关于此事的博客。本文引用了上一篇文章Every Binary Tree There Is中编写的代码。

  

如果你可以枚举所有的二进制树,那么事实证明你可以列举所有解决方案来解决几十个不同的等价问题。

答案 3 :(得分:1)

Python中的非递归解决方案:

#! /usr/bin/python

def valid(state,N):
    cnt=0
    for i in xrange(N):
        if cnt<0:
            return False
        if (state&(1<<i)):
            cnt+=1
        else:
            cnt-=1
    return (cnt==0)

def make_string(state,N):
    ret=""
    for i in xrange(N):
        if state&(1<<i):
            ret+='{'
        else:
            ret+='}'
    return ret

def all_permuts(N):
    N*=2
    return [make_string(state,N) for state in xrange(1<<N) if valid(state,N)]

if __name__=='__main__':
    print "\n".join(all_permuts(3))

这基本上检查[0,2 ^ n]中每个数字的二进制表示,将'1'视为'{',将'0'视为'}',然后仅过滤掉那些正确的数字均衡。