Á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)
.