【问题标题】:Valid Permutation of Parenthesis [duplicate]括号的有效排列[重复]
【发布时间】:2011-03-11 11:22:01
【问题描述】:

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

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

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

【问题讨论】:

    标签: algorithm


    【解决方案1】:

    问题概述

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

    • 生成所有可能的方法来平衡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):

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

    基本上,我们会跟踪有多少开括号和右括号“库存”供我们在递归构建字符串时使用。

    • 如果没有库存,则字符串已完全构建,您可以将其打印出来
    • 如果库存中有可用的左括号,请尝试添加它。
      • 现在您有 少一个开括号,但 多了一个闭括号来平衡它
    • 如果库存中有右括号,请尝试添加。
      • 现在你少了一个右括号

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


    “优化”变体

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

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

    我们还可以通过简化递归树来进行优化。我们可以只递归“一种方式”,然后迭代地执行“另一种方式”,而不是像原来的解决方案那样递归“双向”。

    在下文中,我们进行了两种优化,使用char[]index 代替String,并且递归只添加左括号,迭代地添加右括号:(see also on ideone.com)

    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 非常简单、优雅的解决方案。也很好的解释。
    • @polygenelubricants,你能增加运行时间复杂度吗?
    • 您与 ideone 的链接已失效...
    【解决方案2】:

    Eric Lippert 最近在他的文章Every Tree There Is 中对此进行了博文。文章参考上一篇文章Every Binary Tree There Is写的代码。

    如果你能枚举出所有的二叉树,那么你就可以枚举出几十个不同等价问题的所有解决方案。

    【讨论】:

      【解决方案3】:

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

      参考

      【讨论】:

      • 但是他在寻找变种而不是有多少。
      • +1;加泰罗尼亚数字确实是相关的。这个问题以许多不同的方式表现出来。
      【解决方案4】:

      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”视为“}”,然后仅过滤掉那些正确的均衡。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-11-01
        • 2022-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-10-10
        • 1970-01-01
        相关资源
        最近更新 更多