Arboles binarios
Un árbol binario es un árbol con nodos de tres tipos:
- nodos terminales, que no tienen hijos
- nodos unarios, que tienen un hijo cada uno
- nodos binarios, que tienen dos hijos cada uno
Podemos representarlos con la siguiente gramática, dada en BNF (forma Backus-Naur):
<e> ::=
<terminal>
| <unary>
| <binary>
<terminal> ::=
"0"
<unary> ::=
"(1" <e> ")"
<binary> ::=
"(2" <e> " " <e> ")"
En esta gramática, los nodos se dan en preorden y cada nodo está representado por un dígito que es el número de hijos que tiene.
Números Motzkin
Los números de Motzkin ( OEIS ) ( Wikipedia ) tienen muchas interpretaciones, pero una interpretación es que el n
número de Motzkin es el número de árboles binarios distintos con n
nodos. Comienza una tabla de números de Motzkin
N Motzkin number M(N)
1 1
2 1
3 2
4 4
5 9
6 21
7 51
8 127
...
por ejemplo, M(5)
es 9, y los nueve árboles binarios distintos con 5 nodos son
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)
Tarea
Tome un solo entero positivo n
como entrada y salida de todos los árboles binarios distintos con n
nodos.
Ejemplos n
de 1 a 5 con paréntesis incluidos para facilitar la lectura
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)
Entrada
La entrada será un entero positivo.
Salida
El resultado debe ser una representación inteligible de los distintos árboles binarios con tantos nodos. No es obligatorio usar la cadena exacta dada por la gramática BNF anterior: es suficiente que la sintaxis utilizada dé una representación inequívoca de los árboles. Por ejemplo, podría usar en []
lugar de ()
un nivel adicional de paréntesis en [[]]
lugar de []
paréntesis externos presentes o faltantes, comas adicionales o sin comas, espacios adicionales, paréntesis o sin paréntesis, etc.
Todos estos son equivalentes:
(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
También una variación propuesta por @xnor en un comentario. Como hay una manera de traducir esto a un formato que se pueda entender, es aceptable.
[[[]][]] is (2 (1 0) 0)
Para que sea más fácil de entender, convierta algunos de los []
que le ()
gusten
[([])()]
Ahora si comienzas con
[]
luego inserta un binario que necesita dos expresiones que obtienes
[()()] which is 2
y luego para el primer () inserta un unario que necesita una expresión que obtienes
[([])()] which is 21
pero como []
o ()
sin corchetes internos puede representar 0 que no necesita más expresiones, puede interpretarlo
2100
Tenga en cuenta que las respuestas deberían funcionar teóricamente con memoria infinita, pero obviamente se quedarán sin memoria para una entrada finita dependiente de la implementación.
Variaciones de salida
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)
Un posible lugar para buscar árboles duplicados
Un lugar para buscar un duplicado es con M (5).
Este árbol se generó dos veces para M (5) a partir de M (4) árboles
(2 (1 0) (1 0))
el primero agregando una rama unaria a
(2 (1 0) 0)
y segundo agregando una rama unaria a
(2 0 (1 0))
Entendiendo BNF
BNF se compone de reglas simples:
<symbol> ::= expression
donde a la izquierda hay un nombre de símbolo rodeado por <>
.
A la derecha está la expresión para construir el símbolo. Algunas reglas usan otras reglas en la construcción, por ej.
<e> ::= <terminal>
e
puede ser un terminal
y algunas reglas tienen caracteres que se usan para construir el símbolo, por ej.
<terminal> ::= "0"
terminal
es solo el caracter cero.
Algunas reglas tienen múltiples formas de construirlas, p. Ej.
<e> ::=
<terminal>
| <unary>
| <binary>
Un e
puede ser a <terminal>
o a <unary>
o a <binary>
.
Y algunas reglas son una secuencia de partes, por ej.
<unary> ::= "(1" <e> ")"
A unary
son los caracteres (1
seguidos de lo que se puede construir e
seguido de )
.
Siempre comienza con la regla de inicio, que para esto <e>
.
Algunos ejemplos simples:
La secuencia más simple es justa 0
. Entonces comenzamos con la regla de inicio <e>
y vemos que hay tres opciones:
<terminal>
| <unary>
| <binary>
así que toma el primero <terminal>
. Ahora una terminal no tiene opciones y es 0
. Así que reemplace <terminal>
con 0
en la <e>
regla y ya está.
Entonces el siguiente es (1 0)
. Comience con <e>
y use la regla <unary>
que tiene
"(1" <e> ")"
Ahora esto necesita un, <e>
así que volvemos <e>
y hacemos una elección de uno de los tres, esta vez eligiendo, lo <terminal>
que da 0
. Reemplazar 0
en (1 <e> )
da (1 0)
, y esto se reemplaza en <unary>
así <e>
es (1 0)
.
fuente
Respuestas:
Haskell, 68 bytes
Los nodos terminales están representados por nodos
0
unarios y binarios por(e)
resp.(ee)
, por lo que los dos árboles de tres nodos se dan como(00)
y((0))
.Ejemplos:
fuente
CJam (37 bytes)
Demo en línea . Tenga en cuenta que esto no es muy eficiente, y probablemente no quiera intentar calcular la entrada en
5
línea.Disección a seguir.
fuente
Pyth (
242119 bytes)Esto se basa en mi solución Python 3 .
Es la primera vez que uso Pyth, por lo que es probable que todavía sea golfable.
Ejemplo , salida cuando la entrada es
4
:1 representa un nodo binario, 0 representa un nodo unario y -1 representa un nodo terminal. Hay un nodo terminal implícito al final de cada árbol.
Explicacion :
fuente
brainfuck, 107 bytes
Formateado:
Pruébalo en línea
La entrada se toma como un byte y el árbol
12100
se representa como\x01\x02\x03\x02
: para volver a convertir, traducirtr/\x01\x02\x03/012/
, invertir la cadena y agregar un final0
. Los árboles están separados por\xfe
. (La salida se puede hacer más fácil de leer, por ejemplo, cambiando el primero-
en-36
y el.
en+47.-47
, donde-36
significa una cadena de 36-
caracteres, etc.)Este enfoque hace uso de la propiedad (que también utilizó Ben Frankel): considerando los posibles nodos como
-1, 0, 1
y sin tener en cuenta el-1
nodo final , una lista representa un árbol válido si y solo si (1) todos los prefijos de la lista tienen una suma no negativa, y (2) la suma de la lista completa es igual0
. La primera condición se mantiene al generar nodos intermedios, por lo que solo la segunda condición debe verificarse al final.La cinta se divide en celdas de 5 nodos,
donde
i
está el índice (descendiendo de izquierda a derecha),d
es la suma parcial yx
es el elemento.Bosquejo del flujo de control:
Tenga en cuenta que a veces un valor se almacena o inicializa como uno o dos mayor que el valor real (conceptual) y se ajusta según sea necesario.
fuente
Python 3 (
138134128121119 bytes)Ejemplo de salida, para
n=5
:1 representa un nodo binario, 0 representa un nodo unario y -1 representa un nodo terminal. Hay un nodo terminal implícito al final de cada árbol.
El programa comienza a tomar demasiado tiempo
n=17
.fuente
JavaScript (Firefox 30-57), 79 bytes
Donde
-1
representa un terminal,0
un nodo unario y1
un nodo binario. Comienza a ser lento enm=14
mi PC. Recursivamente trabaja desde el final del árbol.l
está limitado por el hecho de que solo puede quedar 1 nodo al final.n
está limitado por la necesidad de tener suficientes nodos no contabilizados para ser sus hijos.fuente
Prolog,
149144138137131107 bytesY para contar las soluciones
fuente
Python, 71 bytes
Esto representa los árboles como tuplas anidadas, como
((((),), ()),)
, que se pueden transformar((())())
eliminando comas, espacios y lo más externo()
.Una versión anterior de 76 bytes:
fuente
CJam, 38 bytes
Utiliza un enfoque diferente que la respuesta CJam de Peter Taylor.
La salida será algo así
1110120020102100
. Cada árbol es un grupo den
dígitos (donden
está el número de entrada).La idea básica es que generamos cada posible cadena de dígitos
0
,1
y2
, a continuación, filtrar sólo los que son árboles bien formados.fuente