Árvores binárias
Uma árvore binária é uma árvore com nós de três tipos:
- nós terminais, que não têm filhos
- nós unários, que têm um filho cada
- nós binários, que têm dois filhos cada
Podemos representá-los com a seguinte gramática, dada em BNF (forma Backus – Naur):
<e> ::=
<terminal>
| <unary>
| <binary>
<terminal> ::=
"0"
<unary> ::=
"(1" <e> ")"
<binary> ::=
"(2" <e> " " <e> ")"
Nesta gramática, os nós são fornecidos em pré-encomenda e cada nó é representado por um dígito, que é o número de filhos que possui.
Números de Motzkin
Os números de Motzkin ( OEIS ) ( Wikipedia ) têm muitas interpretações, mas uma interpretação é que o n
número de Motzkin é o número de árvores binárias distintas com n
nós. Uma tabela de números de Motzkin começa
N Motzkin number M(N)
1 1
2 1
3 2
4 4
5 9
6 21
7 51
8 127
...
por exemplo, M(5)
é 9 e as nove árvores binárias distintas com 5 nós são
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)
Tarefa
Pegue um número inteiro positivo único n
como entrada e saída de todas as árvores binárias distintas com n
nós.
Exemplos n
de 1 a 5 com parênteses incluídos para facilitar a leitura
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
A entrada será um número inteiro positivo.
Saída
A saída deve ser uma representação inteligível das distintas árvores binárias com tantos nós. Não é obrigatório usar a sequência exata dada pela gramática BNF acima: é suficiente que a sintaxe usada forneça uma representação inequívoca das árvores. Por exemplo, você pode usar em []
vez de ()
, um nível extra de colchetes em [[]]
vez de []
, parênteses externos estão presentes ou ausentes, vírgulas extras ou sem vírgulas, espaços extras, parênteses ou sem parênteses, etc.
Todos estes são 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
Também uma variação proposta por @xnor em um comentário. Como existe uma maneira de traduzir isso para um formato que possa ser entendido, é aceitável.
[[[]][]] is (2 (1 0) 0)
Para tornar isso mais fácil de entender, converta alguns dos []
que ()
quiserem
[([])()]
Agora, se você começar com
[]
depois insira um binário que precisa de duas expressões que você obtém
[()()] which is 2
e depois para o primeiro () insira um unário que precise de uma expressão que você obtém
[([])()] which is 21
mas como []
ou ()
sem bracketing interno pode representar 0, que não precisa de mais expressões, você pode interpretá-lo como
2100
Observe que as respostas devem funcionar teoricamente com memória infinita, mas obviamente ficarão sem memória para uma entrada finita dependente da implementação.
Variações da produção
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)
Um possível local para procurar árvores duplicadas
Um lugar para procurar uma duplicata é com M (5).
Essa árvore foi gerada duas vezes para M (5) a partir de M (4).
(2 (1 0) (1 0))
o primeiro adicionando um ramo unário ao
(2 (1 0) 0)
e segundo adicionando um ramo unário ao
(2 0 (1 0))
Entendendo o BNF
O BNF é composto por regras simples:
<symbol> ::= expression
onde à esquerda é um nome de símbolo cercado por <>
.
À direita está a expressão para construir o símbolo. Algumas regras usam outras regras na construção, por exemplo
<e> ::= <terminal>
e
pode ser um terminal
e algumas regras têm caracteres que são usados na construção do símbolo, por exemplo
<terminal> ::= "0"
terminal
é apenas o caractere zero.
Algumas regras têm várias maneiras de construí-las, por exemplo
<e> ::=
<terminal>
| <unary>
| <binary>
Um e
pode ser um <terminal>
ou um <unary>
ou um <binary>
.
E algumas regras são uma sequência de partes, por exemplo
<unary> ::= "(1" <e> ")"
A unary
são os caracteres (1
seguidos pelo que pode ser construído e e
seguido por )
.
Você sempre começa com a regra inicial, que para isso <e>
.
Alguns exemplos simples:
A sequência mais simples é justa 0
. Então começamos com a regra inicial <e>
e vemos que há três opções:
<terminal>
| <unary>
| <binary>
então pegue o primeiro <terminal>
. Agora, um terminal não tem opções e é 0
. Então substitua <terminal>
por 0
na <e>
regra e pronto.
Então o próximo é (1 0)
. Comece com <e>
e use a regra <unary>
que possui
"(1" <e> ")"
Agora, isso precisa de um, <e>
para que voltemos <e>
ae escolha um dos três, desta vez escolhendo, o <terminal>
que dá 0
. Substituir 0
em (1 <e> )
doações (1 0)
, e isso é substituído <unary>
pelo que <e>
é (1 0)
.
fonte
Respostas:
Haskell, 68 bytes
Nós terminais são representados por nós
0
unários e binários por(e)
resp.(ee)
, portanto, as duas árvores de três nós são fornecidas como(00)
e((0))
.Exemplos:
fonte
CJam (37 bytes)
Demonstração online . Observe que isso não é muito eficiente e você provavelmente não deseja tentar calcular a entrada
5
online.Dissecção a seguir.
fonte
Pitão (
242119 bytes)Isso é baseado na minha solução Python 3 .
É a minha primeira vez usando Pyth, então isso provavelmente ainda é jogável.
Exemplo , saída quando a entrada é
4
:1 representa um nó binário, 0 representa um nó unário e -1 representa um nó terminal. Há um nó terminal implícito no final de cada árvore.
Explicação :
fonte
brainfuck, 107 bytes
Formatado:
Experimente online
A entrada é aceita como um byte , e a árvore
12100
é representada como\x01\x02\x03\x02
: para converter de volta, convertertr/\x01\x02\x03/012/
, inverter a sequência e adicionar uma final0
. As árvores são separadas por\xfe
. (A saída pode ser mais fácil de ler, alterando, por exemplo, o primeiro-
para dentro-36
e o.
para+47.-47
, onde-36
significa uma sequência de 36-
caracteres, etc.)Essa abordagem utiliza a propriedade (que Ben Frankel também usou): considerando os possíveis nós
-1, 0, 1
e desconsiderando o-1
nó final , uma lista representa uma árvore válida se e somente se (1) todos os prefixos da lista tiverem soma não negativa, e (2) a soma da lista inteira é igual0
. A primeira condição é mantida ao gerar nós intermediários, portanto, apenas a segunda condição precisa ser verificada no final.A fita é dividida em células de 5 nós,
onde
i
é o índice (descendente da esquerda para a direita),d
é a soma parcial ex
é o elementoEsboço do fluxo de controle:
Observe que algumas vezes um valor é armazenado ou inicializado como um ou dois maiores que o valor real (conceitual) e ajustado conforme necessário.
fonte
Python 3 (
138134128121119 bytes)Exemplo de saída, para
n=5
:1 representa um nó binário, 0 representa um nó unário e -1 representa um nó terminal. Há um nó terminal implícito no final de cada árvore.
O programa começa a demorar muito tempo
n=17
.fonte
JavaScript (Firefox 30-57), 79 bytes
Onde
-1
representa um terminal,0
um nó unário e1
um nó binário. Começa a ficar lento nom=14
meu PC. Recursivamente trabalha de volta do final da árvore.l
é limitado pelo fato de que pode haver apenas 1 nó no final.n
é limitado pela necessidade de ter nós não contabilizados suficientes para serem seus filhos.fonte
Prolog,
149144138137 137131107 bytesE para contar as soluções
fonte
Python, 71 bytes
Isso representa árvores como tuplas aninhadas, como
((((),), ()),)
, que podem ser transformadas((())())
removendo vírgulas, espaços e áreas externas()
.Uma versão anterior de 76 bytes:
fonte
CJam, 38 bytes
Usa uma abordagem diferente que a resposta CJam de Peter Taylor.
Saída será algo parecido
1110120020102100
. Cada árvore é um grupo den
dígitos (onden
está o número de entrada).A idéia básica é que geramos cada possível seqüência de dígitos
0
,1
e2
, em seguida, filtrar apenas os que são árvores bem formados.fonte