二叉树
二叉树是具有三种类型的节点的树:
- 没有子节点的终端节点
- 一元节点,每个节点有一个孩子
- 二进制节点,每个都有两个子节点
我们可以用BNF(巴克斯-纳尔形式)中给出的以下语法来表示它们:
<e> ::=
<terminal>
| <unary>
| <binary>
<terminal> ::=
"0"
<unary> ::=
"(1" <e> ")"
<binary> ::=
"(2" <e> " " <e> ")"
在此语法中,节点是按顺序排列的,每个节点都由一个数字表示,该数字表示其拥有的子代数。
莫兹金数
Motzkin数(OEIS)(Wikipedia)有多种解释,但一种解释是,n
Motzkin数是具有n
节点的不同二叉树的数目。莫兹金数表开始
N Motzkin number M(N)
1 1
2 1
3 2
4 4
5 9
6 21
7 51
8 127
...
例如M(5)
9,而9个具有5个节点的二叉树是
1 (1 (1 (1 (1 0))))
2 (1 (1 (2 0 0)))
3 (1 (2 0 (1 0)))
4 (1 (2 (1 0) 0))
5 (2 0 (1 (1 0)))
6 (2 0 (2 0 0))
7 (2 (1 0) (1 0))
8 (2 (1 (1 0)) 0)
9 (2 (2 0 0) 0)
任务
以单个正整数n
作为输入,并输出所有带有n
节点的不同二叉树。
示例n
1到5带有括号,以提高可读性
0
(1 0)
(1 (1 0))
(2 0 0)
(1 (1 (1 0)))
(1 (2 0 0))
(2 0 (1 0))
(2 (1 0) 0)
(1 (1 (1 (1 0))))
(1 (1 (2 0 0)))
(1 (2 0 (1 0)))
(1 (2 (1 0) 0))
(2 0 (1 (1 0)))
(2 0 (2 0 0))
(2 (1 0) (1 0))
(2 (1 (1 0)) 0)
(2 (2 0 0) 0)
输入值
输入将是一个正整数。
输出量
输出应该是具有那么多节点的不同二叉树的可理解表示。使用上面的BNF语法给出的确切字符串不是强制性的:使用的语法给出树的明确表示就足够了。例如,你可以使用[]
的替代()
,支架额外级别的[[]]
代替[]
,外括号存在或缺失,额外的逗号或没有逗号,多余的空格,括号或没有括号,等等。
所有这些都是等效的:
(1 (2 (1 0) 0))
[1 [2 [1 0] 0]]
1 2 1 0 0
12100
(1 [2 (1 0) 0])
.:.--
*%*55
(- (+ (- 1) 1))
-+-11
也是@xnor在评论中使用的变体。由于有一种方法可以将其转换为可以理解的格式,因此可以接受。
[[[]][]] is (2 (1 0) 0)
为了使这更容易理解一些转换的[]
到()
像现在这样
[([])()]
现在,如果您开始
[]
然后插入一个需要两个表达式的二进制文件
[()()] which is 2
然后为第一个()插入一个需要一个表达式的一元
[([])()] which is 21
但由于有[]
或()
没有内括号可以表示0,因此不需要更多表达式,您可以将其解释为
2100
请注意,答案应在理论上适用于无限内存,但对于依赖于实现的有限输入,显然会用光内存。
输出变化
BNF xnor Christian Ben
b(t, b(t, t)) [{}{{}{}}] (0(00)) (1, -1, 1, -1)
b(t, u(u(t))) [{}{(())}] (0((0))) (1, -1, 0, 0)
b(u(t), u(t)) [{()}{()}] ((0)(0)) (1, 0, -1, 0)
b(b(t, t), t) [{{}{}}{}] ((00)0) (1, 1, -1, -1)
b(u(u(t)), t) [{(())}{}] (((0))0) (1, 0, 0, -1)
u(b(t, u(t))) [({}{()})] ((0(0))) (0, 1, -1, 0)
u(b(u(t), t)) [({()}{})] (((0)0)) (0, 1, 0, -1)
u(u(b(t, t))) [(({}{}))] (((00))) (0, 0, 1, -1)
u(u(u(u(t)))) [(((())))] ((((0)))) (0, 0, 0, 0)
一个可能检查重复树的地方
一个检查重复项的地方是M(5)。
从M(4)个树中为M(5)生成了这棵树两次
(2 (1 0) (1 0))
首先通过添加一元分支到
(2 (1 0) 0)
其次,通过添加一元分支到
(2 0 (1 0))
了解BNF
BNF由以下简单规则组成:
<symbol> ::= expression
左边是用包围的符号名称<>
。
右边是用于构造符号的表达式。一些规则在构造中使用其他规则,例如
<e> ::= <terminal>
e
可以是 terminal
某些规则具有用于构造符号的字符,例如
<terminal> ::= "0"
terminal
只是字符零。
一些规则具有多种构建方式,例如
<e> ::=
<terminal>
| <unary>
| <binary>
一个e
可以是一个<terminal>
或一个<unary>
或一个<binary>
。
有些规则是一系列的部分,例如
<unary> ::= "(1" <e> ")"
一个unary
是人物(1
,然后什么可以构造e
之后)
。
您总是从起始规则开始,为此<e>
。
一些简单的例子:
最简单的序列是just 0
。因此,我们从起始规则开始<e>
,看看有三个选择:
<terminal>
| <unary>
| <binary>
所以拿第一个<terminal>
。现在一个终端别无选择,是0
。因此<terminal>
,0
在<e>
规则中替换为,您就完成了。
然后下一个是(1 0)
。首先<e>
使用<unary>
具有
"(1" <e> ")"
现在,这需要一个<e>
,所以我们回去<e>
,使三者之一的选择,这次选择,<terminal>
这给0
。更换0
进(1 <e> )
给(1 0)
,这被替换成<unary>
这样<e>
的(1 0)
。