Бинарные деревья
Бинарное дерево - это дерево с узлами трех типов:
- терминальные узлы, которые не имеют детей
- унарные узлы, каждый из которых имеет одного ребенка
- двоичные узлы, каждый из которых имеет двоих детей
Мы можем представить их с помощью следующей грамматики, приведенной в BNF (форма Бэкуса – Наура):
<e> ::=
<terminal>
| <unary>
| <binary>
<terminal> ::=
"0"
<unary> ::=
"(1" <e> ")"
<binary> ::=
"(2" <e> " " <e> ")"
В этой грамматике узлы даны в предзаказе, и каждый узел представлен цифрой, которая является числом дочерних элементов, которые у него есть.
Числа Моцкина
Числа Моцкина ( OEIS ) ( Википедия ) имеют много интерпретаций, но одна интерпретация состоит в том, что n
число Моцкина является числом различных двоичных деревьев с 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, и девять различных двоичных деревьев с 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 (5) из M (4) деревьев
(2 (1 0) (1 0))
первый, добавив унарную ветвь к
(2 (1 0) 0)
и во-вторых, добавив унарную ветвь к
(2 0 (1 0))
Понимание БНФ
БНФ состоит из простых правил:
<symbol> ::= expression
где слева это имя символа окружения <>
.
Справа - выражение для построения символа. Некоторые правила используют другие правила в конструкции, например
<e> ::= <terminal>
e
может быть terminal
и некоторые правила имеют символы, которые используются при построении символа, например
<terminal> ::= "0"
terminal
это просто символ ноль.
Некоторые правила имеют несколько способов их построения, например
<e> ::=
<terminal>
| <unary>
| <binary>
An e
может быть <terminal>
или a <unary>
или a <binary>
.
И некоторые правила представляют собой последовательность частей, например,
<unary> ::= "(1" <e> ")"
A unary
- символы, (1
за которыми следует то, для чего можно построить, e
а затем )
.
Вы всегда начинаете со стартового правила, которое для этого <e>
.
Несколько простых примеров:
Самая простая последовательность просто 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)
.