递归方法:我们如何生成括号上的所有可能性?
我们怎样才能在大括号上生成所有可能性?
N值已经给了我们,我们要产生所有的可能性。
示例:
1) 如果 N == 1,则只有一种可能性 () 。
2) 如果 N==2,则可能性为 (())、()()
3) 如果 N==3,则可能性为 ((()))、(())()、()()( ), ()(()) ...
注意:左右大括号应该匹配。我的意思是 )( 对于 N==1 是无效的
我们可以通过使用递归方法来解决这个问题吗?
How can we generate all possibilities on braces ?
N value has given to us and we have to generate all possibilities.
Examples:
1) if N == 1, then only one possibility () .
2) if N==2, then possibilities are (()), ()()
3) if N==3, then possibilities are ((())), (())(),()()(), ()(()) ...
Note: left and right braces should match. I mean )( is INVALID for the N==1
Can we solve this problem by using recurrence approach ?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
来自维基百科 -
另请参阅 http://www.acta.sapientia.ro/acta-info/C1-1/info1-9.pdf
From wikipedia -
See also http://www.acta.sapientia.ro/acta-info/C1-1/info1-9.pdf
对于给定的
N
,我们始终必须以左大括号开头。现在考虑它对应的右大括号在哪里。它可以位于中间,如()()
中,也可以位于末尾,如(())
中的N=2
。现在考虑
N=3
:它可以位于末尾:
(()())
和((()))
。或者在中间:
()(())
和()()()
,它位于位置 2。那么它也可以位于位置 4:(())()
。现在我们基本上可以将这两种情况结合起来,实现右大括号位于末尾的情况与位于中间的情况相同,但将 N=0 的所有可能性添加到末尾。
现在要解决这个问题,您可以计算出开始和结束大括号之间的
n
的所有可能性,类似地,您可以计算出结束大括号之后的m
的所有可能性。 (注意m+n+1 = N
)然后您可以组合所有可能的组合,将它们附加到您的可能性列表中,然后继续移动到端括号的下一个可能位置。请注意,处理此类问题时很容易犯的错误是找到
i
和Ni
的所有可能性,然后将它们组合起来,但这对于N=3
会重复计算()()()
或至少打印两次。下面是一些解决该问题的 Python 2.x 代码:
For a given
N
we always have to start with an open brace. Now consider where it's corresponding closing brace is. It can be in the middle like in()()
or at the end like(())
forN=2
.Now consider
N=3
:It can be at the end:
(()())
and((()))
.Or in the middle:
()(())
and()()()
where it's in position 2. Then it can also be in position 4:(())()
.Now we can essentially combine the 2 cases by realising the case where the closing brace is at the end is the same as it being at the middle, but with all the possibilities for N=0 added to the end.
Now to solve it you can work out all the possibilities for
n
between the begin and end brace and similarly you can work out all the possibilities form
after the end brace. (Notem+n+1 = N
) Then you can just combine all possible combinations, append them to your list of possibilities and move on to the next possible location for the end brace.Just be warned an easy mistake to make with these types of problems, is to find all the possibilities for
i
and forN-i
and just combine them, but this forN=3
would double count()()()
or at least print it twice.Here is some Python 2.x code that solves the problem:
递归解:
Recursive solution:
下面的一些代码本质上是 JPvdMerwe 代码的紧凑版本,只不过它返回解决方案列表而不是打印它们。该代码适用于 Python 2 和 Python 3。
输出
这里还有几个函数,源自 Ed Guness 链接的文章中给出的伪代码: acta.sapientia.ro/acta-info/C1-1/info1-9.pdf" rel="nofollow noreferrer">Dyck 词的生成和排名。该文章使用基于 1 的索引,但我已将它们转换为符合 Python 的基于 0 的索引。
这些函数比上面的
solve
函数慢,但它们可能仍然有用。 pos_dyck_words 的优点是它是纯粹迭代的。unrank
是迭代的,但它调用递归辅助函数f
; OTOH,f
使用缓存,所以它并没有那么慢,而且它只是缓存整数,它比solve
的字符串缓存使用更少的 RAM。unrank
的主要好处是它可以从索引号返回单个解决方案,而不必生成给定大小的所有解决方案。此代码仅适用于 Python 3。将其转换为 Python 2 使用非常容易,您只需实现自己的缓存方案而不是
lru_cache
即可。您确实需要缓存,否则f
对于除了最小的Dyck字长之外的所有字长来说都慢得难以忍受。输出
Here's some code that's essentially a compact version of JPvdMerwe's code, except that it returns the list of solutions rather than printing them. This code works on both Python 2 and Python 3.
output
Here are a couple more functions, derived from the pseudocode given in the article linked by Ed Guiness: Generating and ranking of Dyck words. That article uses 1-based indexing, but I've converted them to conform with Python's 0-based indexing.
These functions are slower than the
solve
function above, but they may still be useful.pos_dyck_words
has the advantage that it's purely iterative.unrank
is iterative, but it calls the recursive helper functionf
; OTOH,f
uses caching, so it's not as slow as it could be, and it's only caching integers, which uses less RAM than the string caching ofsolve
. The main benefit ofunrank
is that it can return an individual solution from its index number rather than having to produce all solutions of a given size.This code is for Python 3 only. It's easy enough to convert it for Python 2 use, you just have to implement your own caching scheme instead of
lru_cache
. You do need caching, otherwisef
is intolerably slow for all but the smallest Dyck word lengths.output
我提出了以下算法,该算法不是按照 OP 的要求进行递归的,但鉴于其无敌的效率,值得一提。
正如 Ed Guness' 中所述 post,
N
对正确匹配的括号组成的字符串是 Dyck 单词的表示。在另一种有用的表示中,括号(
和)
分别替换为1
和0
。因此,()()()
变为101010
。后者也可以看作是(十进制)数字42
的二进制表示。总之,一些整数可以表示正确匹配的括号对的字符串。使用这种表示,以下是生成 Dyck 作品的有效算法。令
integer
为任何 C/C++(或者可能是 C 系列编程语言)最长 64 位的无符号整数类型。给定一个 Dyck 单词,以下代码将返回下一个相同大小的 Dyck 单词(前提是它存在)。例如,如果
w == 42
(二进制的101010
,即()()()
),则函数返回44
(101100
、()(())
)。可以迭代,直到得到56
(111000
,((()))
),这是N == 的最大 Dyck 单词3.
。上面,我提到了无敌的效率,因为就单个 Dyck 词的生成而言,该算法是 O(1)、无循环、无分支的。然而,实施仍有改进的空间。事实上,如果我们可以使用一些严格符合标准的 C/C++ 中不可用的汇编指令,则可以消除函数体中相对昂贵的除法
c / a
。你可能会说。 “反对!我不想受到
N <= 64
的限制”。好吧,我对此的回答是,如果您想生成所有 Dyck 作品,那么实际上您已经绑定了比64
小得多的大小。事实上,戴克作品大小N
的数量呈阶乘增长N
并且对于N == 64
生成它们的时间可能比宇宙的年龄还要长。 (我承认我这次没有计算,但这是此类性质问题的一个非常常见的轶事特征。)我写了一个 算法的详细文档。
I came up with the following algorithm which is not recursive as requested by the OP but it is worth mentioning given its invincible efficiency.
As said in Ed Guiness' post, strings of
N
pairs of correctly matched parentheses is a representation of a Dyck word. In another useful representation, parentheses(
and)
are replaced with1
and0
respectively. Hence,()()()
becomes101010
. The latter can also be seen as the binary representation of (decimal) number42
. In summary, some integer numbers can represent strings of correctly matched pairs of parentheses. Using this representation the following is an efficient algorithm to generate Dyck works.Let
integer
be any C/C++ (or, possibly, and member of the the C-family programming languages) unsigned integer type up to 64-bits long. Given a Dyck word the following code returns the next Dyck word of the same size, provided it exists.For instance, if
w == 42
(101010
in binary, i.e.,()()()
) the function returns44
(101100
,()(())
). One can iterate until getting56
(111000
,((()))
) which is the maximum Dyck word forN == 3
.Above, I've mentioned invincible efficiency because, as far as generation of a single Dyck word is concerned, this algorithm is O(1), loopless and branchless. However, the implementation still has room for improvement. Indeed, the relatively expensive division
c / a
in the body of the function can be eliminated if we can use some assembly instructions that are not available in strict Standard compliant C/C++.You might say. "Objection! I do not want to be constraint to
N <= 64
". Well, my answer to that is that if you want to generate all Dyck works, then in practice you are already bound to a much lower size than64
. Indeed, the number of Dyck works of sizeN
grows factorially withN
and forN == 64
the time to generate them all is likely to be greater than the age of the universe. (I confess I did not calculate this time but this is a quite common anecdotal feature of problems of this nature.)I've written a detailed document on the algorithm.