Algorithm 递归方法:如何生成大括号上的所有可能性?
我们如何生成大括号上的所有可能性 N值已经给了我们,我们必须产生所有的可能性 示例: 1) 如果N==1,则只有一种可能性() 2) 如果N==2,则可能性为(()),()() 3) 如果N==3,那么可能性是((()),(()),()()(),()(),()(()) 注意:左大括号和右大括号应该匹配。我的意思是)(对于N==1是无效的Algorithm 递归方法:如何生成大括号上的所有可能性?,algorithm,recursion,data-structures,catalan,Algorithm,Recursion,Data Structures,Catalan,我们如何生成大括号上的所有可能性 N值已经给了我们,我们必须产生所有的可能性 示例: 1) 如果N==1,则只有一种可能性() 2) 如果N==2,则可能性为(()),()() 3) 如果N==3,那么可能性是((()),(()),()()(),()(),()(()) 注意:左大括号和右大括号应该匹配。我的意思是)(对于N==1是无效的 我们可以用递归方法解决这个问题吗?< /P> < P>对于给定的 n>代码>,我们必须从一个开放的括号开始。现在考虑它对应的闭合括号在哪里。它可以在中间,如()
我们可以用递归方法解决这个问题吗?< /P> < P>对于给定的<代码> n>代码>,我们必须从一个开放的括号开始。现在考虑它对应的闭合括号在哪里。它可以在中间,如<代码>())/代码>或在结尾,如<代码>(())< /代码> <代码> n=2 < /C> >
现在考虑<代码> n=3 < /代码>:
它可以在末尾:(())
和(())
或者在中间:()(())
和()
位置2。然后它也可以在位置4:(())()
现在,我们可以将这两种情况结合起来,实现闭合支撑位于末端的情况与位于中间的情况相同,但在末端添加了N=0的所有可能性
现在,为了解决这个问题,您可以计算出开始和结束支撑之间n
的所有可能性,同样,您可以计算出结束支撑之后m
的所有可能性。(注意m+n+1=n
)然后,您可以组合所有可能的组合,将它们附加到可能的列表中,然后继续移动到端部支撑的下一个可能位置
请注意,对于这些类型的问题,容易犯的一个错误是找到i
和N-i
的所有可能性,然后将它们组合起来,但是对于N=3
来说,这将加倍计算()
,或者至少打印两次
下面是一些解决此问题的Python 2.x代码:
memoise = {}
memoise[0] = [""]
memoise[1] = ["()"]
def solve(N, doprint=True):
if N in memoise:
return memoise[N]
memoise[N] = []
for i in xrange(1,N+1):
between = solve(i-1, False)
after = solve(N-i, False)
for b in between:
for a in after:
memoise[N].append("("+b+")"+a)
if doprint:
for res in memoise[N]:
print res
return memoise[N]
来自维基百科-
Dyck字是由nx和ny组成的字符串,因此该字符串的初始段的Y数不超过X(另请参见Dyck语言)。例如,以下是长度为6的Dyck字:
将符号X重新解释为开括号,Y重新解释为闭括号,Cn统计包含n对正确匹配的括号的表达式数:
另见
摘要:提出了一种生成所有Dyck字的新算法,
用于对Dyck单词进行排序和取消排序。我们强调
在编码加泰罗尼亚语相关对象时使用Dyck词的重要性
数字。作为排名算法中使用的公式的结果
我们可以得到第n个加泰罗尼亚数的递归公式
递归解决方案:
import java.util.Scanner;
public class Parentheses
{
static void ParCheck(int left,int right,String str)
{
if (left == 0 && right == 0)
{
System.out.println(str);
}
if (left > 0)
{
ParCheck(left-1, right+1 , str + "(");
}
if (right > 0)
{
ParCheck(left, right-1, str + ")");
}
}
public static void main(String[] args)
{
Scanner input=new Scanner(System.in);
System.out.println("Enter the number");
int num=input.nextInt();
String str="";
ParCheck(num,0,str);
}
}
这里的一些代码本质上是JPvdMerwe代码的压缩版本,只是它返回解决方案列表而不是打印它们 来自itertools导入产品的
def solve(num,cache={0:['']}):
如果num不在缓存中:
缓存[num]=['(%s)%s'%t用于范围(1,num+1)中的i
对于乘积中的t(solve(i-1),solve(num-i))]
返回缓存[num]
#试验
对于范围(1,5)中的num:
打印(个)
对于求解中的s(num):
印刷品
输出
1
()
2
()()
(())
3
()()()
()(())
(())()
(()())
((()))
4
()()()()
()()(())
()(())()
()(()())
()((()))
(())()()
(())(())
(()())()
((()))()
(()()())
(()(()))
((())())
((()()))
(((())))
num: 4, Catalan number: 14
14
0 [1, 3, 5, 7] [1, 3, 5, 7] 01010101 True
1 [1, 3, 6, 7] [1, 3, 6, 7] 01010011 True
2 [1, 4, 5, 7] [1, 4, 5, 7] 01001101 True
3 [1, 4, 6, 7] [1, 4, 6, 7] 01001011 True
4 [1, 5, 6, 7] [1, 5, 6, 7] 01000111 True
5 [2, 3, 5, 7] [2, 3, 5, 7] 00110101 True
6 [2, 3, 6, 7] [2, 3, 6, 7] 00110011 True
7 [2, 4, 5, 7] [2, 4, 5, 7] 00101101 True
8 [2, 4, 6, 7] [2, 4, 6, 7] 00101011 True
9 [2, 5, 6, 7] [2, 5, 6, 7] 00100111 True
10 [3, 4, 5, 7] [3, 4, 5, 7] 00011101 True
11 [3, 4, 6, 7] [3, 4, 6, 7] 00011011 True
12 [3, 5, 6, 7] [3, 5, 6, 7] 00010111 True
13 [4, 5, 6, 7] [4, 5, 6, 7] 00001111 True
num: 10, Catalan number: 16796
ok
这里还有几个函数,它们来自Ed Guinness链接的文章中给出的伪代码:。那篇文章使用基于1的索引,但我已将它们转换为符合Python基于0的索引 这些函数比上面的
solve
函数慢,但它们可能仍然有用。pos\u dyck\u words
的优点是它完全是迭代的。unrank
是迭代的,但它调用递归助手函数f
;OTOH,f
使用缓存,因此它不会像可能的那样慢,而且它是s仅缓存整数,与solve
的字符串缓存相比,它使用更少的RAM。unrank
的主要优点是它可以从索引号返回单个解决方案,而不必生成给定大小的所有解决方案
此代码仅适用于Python3。将其转换为Python2使用非常简单,您只需实现自己的缓存方案,而不是lru\u cache
。您确实需要缓存,否则f
对于除最小Dyck字长以外的所有字长来说都非常慢
来自itertools导入产品的
从functools导入lru\U缓存
#递归生成长度为2*num的所有Dyck字
#最快,但不是按字典顺序排列的
def solve(num,cache={0:['']}):
如果num不在缓存中:
缓存[num]=['0%s1%s'%t,用于范围(1,num+1)中的i
对于乘积中的t(solve(i-1),solve(num-i))]
返回缓存[num]
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#“unrank”的辅助函数`
#f(i,j)给出了(0,0)和(i,j)之间不相交的路径数
#网格的对角线x==y。路径由水平和垂直两部分组成
#仅限线段,不允许使用对角线
@lru_缓存(无)
定义f(i,j):
如果j==0:
返回1
如果j==1:
返回i
#如果i #1
如中所述,N
对正确匹配的括号字符串是Dyck单词的表示形式。在另一种有用的表示形式中,括号(
和)
分别替换为1
和0
。因此,()()
变成101010
。后者也可以看作是(十进制)数42
的二进制表示。总之,一些整数可以表示str
1
()
2
()()
(())
3
()()()
()(())
(())()
(()())
((()))
4
()()()()
()()(())
()(())()
()(()())
()((()))
(())()()
(())(())
(()())()
((()))()
(()()())
(()(()))
((())())
((()()))
(((())))
num: 4, Catalan number: 14
14
0 [1, 3, 5, 7] [1, 3, 5, 7] 01010101 True
1 [1, 3, 6, 7] [1, 3, 6, 7] 01010011 True
2 [1, 4, 5, 7] [1, 4, 5, 7] 01001101 True
3 [1, 4, 6, 7] [1, 4, 6, 7] 01001011 True
4 [1, 5, 6, 7] [1, 5, 6, 7] 01000111 True
5 [2, 3, 5, 7] [2, 3, 5, 7] 00110101 True
6 [2, 3, 6, 7] [2, 3, 6, 7] 00110011 True
7 [2, 4, 5, 7] [2, 4, 5, 7] 00101101 True
8 [2, 4, 6, 7] [2, 4, 6, 7] 00101011 True
9 [2, 5, 6, 7] [2, 5, 6, 7] 00100111 True
10 [3, 4, 5, 7] [3, 4, 5, 7] 00011101 True
11 [3, 4, 6, 7] [3, 4, 6, 7] 00011011 True
12 [3, 5, 6, 7] [3, 5, 6, 7] 00010111 True
13 [4, 5, 6, 7] [4, 5, 6, 7] 00001111 True
num: 10, Catalan number: 16796
ok
integer next_dyck_word(integer w) {
integer const a = w & -w;
integer const b = w + a;
integer c = w ^ b;
c = (c / a >> 2) + 1;
c = ((c * c - 1) & 0xaaaaaaaaaaaaaaaa) | b;
return c;
}