Бинарные деревья
Бинарное дерево - это дерево с узлами трех типов:
- терминальные узлы, которые не имеют детей
- унарные узлы, каждый из которых имеет одного ребенка
- двоичные узлы, каждый из которых имеет двоих детей
Мы можем представить их с помощью следующей грамматики, приведенной в 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)
.
источник
Ответы:
Haskell, 68 байт
Терминальные узлы представлены
0
одинарными и двоичными узлами(e)
соответственно.(ee)
, поэтому два трехузловых дерева заданы как(00)
и((0))
.Примеры:
источник
CJam (37 байт)
Демо онлайн . Обратите внимание, что это не очень эффективно, и вы, вероятно, не хотите пытаться вычислять ввод
5
онлайн.Рассечение, чтобы следовать.
источник
Pyth (
242119 байт)Это основано на моем решении Python 3 .
Это мой первый раз, когда я использую Pyth, так что, вероятно, он все еще пригоден для игры в гольф.
Пример , вывод, когда ввод
4
:1 представляет двоичный узел, 0 представляет унарный узел, а -1 представляет терминальный узел. В конце каждого дерева есть подразумеваемый конечный узел.
Пояснение :
источник
брейкфак, 107 байт
отформатирован:
Попробуйте онлайн
Входные данные принимаются в виде байта , а дерево
12100
представляется как\x01\x02\x03\x02
: для обратного преобразования, переводаtr/\x01\x02\x03/012/
, обращения строки и добавления финала0
. Деревья разделены\xfe
. (Вывод можно сделать более простым для чтения, например, изменив первый-
на-36
и.
на+47.-47
, где-36
означает строку из 36-
символов и т. Д.)Этот подход использует свойство (которое также использовал Бен Франкель): рассмотрение возможных узлов
-1, 0, 1
и игнорирование конечного-1
узел, список представляет действительное дерево тогда и только тогда, когда (1) все префиксы списка имеют неотрицательную сумму, и (2) сумма всего списка равна0
. Первое условие сохраняется при создании промежуточных узлов, поэтому в конце необходимо проверить только второе условие.Лента делится на 5-узловые ячейки,
где
i
индекс (по убыванию слева направо),d
частичная сумма иx
элемент.Эскиз потока управления:
Обратите внимание, что иногда значение сохраняется или инициализируется как одно или два больше фактического (концептуального) значения и корректируется по мере необходимости.
источник
Python 3 (
138134128121119 байт)Пример вывода для
n=5
:1 представляет двоичный узел, 0 представляет унарный узел, а -1 представляет терминальный узел. В конце каждого дерева есть подразумеваемый конечный узел.
Программа начинает занимать слишком много времени
n=17
.источник
JavaScript (Firefox 30-57), 79 байт
Где
-1
представляет терминал,0
унарный узел и1
двоичный узел.m=14
На моем ПК начинает медленно работать. Рекурсивно работает обратно с конца дерева.l
ограничено тем фактом, что в конце может быть только 1 узел.n
ограничен необходимостью иметь достаточно неучтенных узлов, чтобы быть его дочерними.источник
Пролог,
149144138137131107 байтИ посчитать решения
источник
Python, 71 байт
Это представляет деревья в виде вложенных кортежей, таких как
((((),), ()),)
, которые могут быть преобразованы((())())
путем удаления запятых, пробелов и внешних элементов()
.Более ранняя 76-байтовая версия:
источник
CJam, 38 байт
Использует другой подход, который отвечает CJam Питера Тейлора.
Выходной будет что-то вроде
1110120020102100
. Каждое дерево представляет собой группуn
цифр (где вводитсяn
номер).Основная идея заключается в том, что мы создаем каждую возможную последовательность цифр
0
,1
и2
, а затем отфильтровать только те, которые хорошо сформированные деревья.источник