É uma árvore linearizada? (Primeira edição da amplitude)

11

fundo

Uma árvore não rotulada pode ser assim:

   o
 / | \
o  o  o
|    / \
o   o   o

Para linearizar essa árvore, primeiro rotulamos cada nó ocom seu número de nós filhos:

   3
 / | \
1  0  2
|    / \
0   0   0

e, em seguida, escreva os números em uma lista da maneira mais rápida possível, significando linha por linha e da esquerda para a direita:

[3, 1, 0, 2, 0, 0, 0]

Esta é uma representação única e inequívoca da árvore acima, o que significa que não existem duas árvores puras diferentes com as mesmas linearizações e que podemos reconstruir a árvore original a partir da lista.

Embora cada árvore corresponda a uma determinada lista inteira, nem cada lista inteira representa uma árvore linearizada válida: por exemplo [2, 0, 0, 0], não representa uma árvore válida, se tentarmos des linearizá-la, terminaremos com essa árvore

[2,0,0,0] -> 2 [0,0,0] -> 2 [0,0] -> 2 [0]
            / \          / \        / \
                        0          0   0

mas ainda tem uma 0esquerda na lista e nenhum lugar para colocá-la. Da mesma forma, [2, 0]também não é uma linearização de árvore válida, pois a árvore não linearizada tem um ponto filho vazio:

  2
 / \
0

Tarefa

Dada uma lista inteira, decida se é uma linearização válida de uma árvore usando o mínimo de bytes possível. Você pode escrever um programa completo ou uma função.

Entrada: uma lista não vazia de números inteiros não negativos.

Saída: Um valor verdadeiro se a lista for uma linearização de uma árvore, caso contrário, um valor falso.

Casos de teste

Truthy
[0]
[2, 0, 0]
[1, 1, 1, 1, 1, 0]
[3, 1, 0, 2, 0, 0, 0]
[2, 0, 2, 2, 0, 0, 2, 0, 0]
[3, 2, 2, 1, 1, 1, 0, 0, 0, 0, 0]
[1, 5, 3, 0, 2, 1, 4, 0, 1, 0, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0]
Falsy
[0, 1]
[2, 0]
[2, 0, 0, 0]
[1, 0, 1]
[3, 2, 1, 0]
[2, 0, 0, 2, 0, 0]
[4, 1, 0, 3, 0, 0, 0, 0]
[4, 2, 0, 3, 1, 0, 0, 0, 0, 0]
Laikoni
fonte

Respostas:

4

Haskell, 44 bytes

f[n:k]=iterate f[k]!!n
f _=[]
g x=f[x]==[[]]

Define uma função gque pega uma lista e retorna um booleano. Veja passar em todos os casos de teste .

Explicação

Isso se baseia no fato de que as linearizações de profundidade primeiro e largura primeiro produzem as mesmas matrizes. Veja as respostas de Martin para detalhes; basicamente, ambos fornecem a mesma condição aritmética na matriz.

A função frecebe a lista de entrada agrupada em uma lista de singleton. Ele sai um número nda lista e depois se chama nvezes na lista restante para processar os filhos do nó popped (profundidade primeiro). A exibição da lista vazia resulta em []que eu uso como estado de erro. A função gverifica se o resultado final é [[]]o estado único e não incorreto, sem nós não processados. Se Haskell foi digitado de maneira fraca, eu poderia simplesmente usar 0ou algo como o estado de erro e não precisaria agrupar a entrada em outra lista.

Zgarb
fonte
3

Mathematica, 38 bytes

Last@#<0<=Min@Most@#&@Accumulate[#-1]&

A idéia básica é que acompanhemos um número de nós a preencher. Cada elemento da lista usa um nó e adiciona quantos tiver filhos. Portanto, cada elemento ialtera a contagem total por i-1. Essa contagem é desativada em um, porque deve começar a partir 1(da raiz), não 0.

Para que a árvore seja válida, a) nunca podemos ficar abaixo 0da lista, porque não teríamos onde colocar o nó atual eb) precisamos terminar -1no final, caso contrário, ainda temos nós não utilizados.

Obtemos esse total em execução dos nós restantes com Accumulate[#-1](que calcula a soma do prefixo da lista de entrada menos um). E então verificamos se o último elemento e apenas o último elemento está -1com:

Last@#<0<=Min@Most@#

Observe que verificar se o último elemento é negativo é suficiente, pois nunca podemos diminuir em mais de 1, portanto, se os últimos valores foram -2inferiores ou inferiores, seria impossível que o mínimo dos outros fosse negativo.

Martin Ender
fonte
2

Retina , 29 bytes

\d+
$*
^(?<-1>(1)*,)*$(?(1)!)

Experimente online! (A primeira linha ativa um conjunto de testes separado por avanço de linha.)

Explicação

A idéia básica é a mesma da minha resposta do Mathematica : mantemos o controle de um total contínuo de nós restantes, garantindo que ele nunca fique abaixo de zero, mas termine em zero. No entanto, a maneira como isso é implementado com o regex é muito diferente.

\d+
$*

Isso simplesmente converte a entrada em unário, transformando cada número inteiro nem n1s.

^(?<-1>(1)*,)*$(?(1)!)

É aqui que a verdadeira magia acontece. É uma expressão regular bastante curta que corresponde apenas a árvores válidas, mas sua mecânica é bastante sutil.

Estou usando grupos de balanceamento para acompanhar o número de nós, que são uma maneira de trabalhar com pilhas dentro do regex.

Primeiro, é claro que essa pilha nunca pode ter uma profundidade negativa, então não podemos realmente acabar com uma representação de -1no final, como fazemos na solução Mathematica. No entanto, podemos observar que o elemento final da entrada deve ser zero em uma pilha válida (caso contrário, não poderíamos terminar com -1). Acontece que ele realmente economiza bytes para verificar tanto que termina em zero e com zero nós restantes.

Então, aqui está um detalhamento da regex:

^        e# Anchor the match to the beginning of the string.
(?<-1>   e# Each repetition of this group will match one number. 
         e# We can ignore the <-1> for now.
  (1)*   e#   Match each unary digit of the current number, pushing
         e#   a capture onto stack 1. This increments our total of
         e#   remaining nodes by 1 for each child.
  ,      e#   Match a comma. Note that this requires that there is at
         e#   least one more number in the list.
)*       e# At the end of the repetition the <-1> pops one capture from
         e# the stack. This is the node that the current number itself
         e# takes up.
$        e# Match the end of the string. This requires the input to end
         e# in a zero, because the last thing we matched was a comma.
(?(1)!)  e# Make sure that stack 1 is empty, so that we don't have any
         e# unused nodes.
Martin Ender
fonte
1

CJam (20 bytes)

{X0@{+\(_0>{\}*}/|!}

Conjunto de testes online . Este é um bloco anônimo que pega uma matriz na pilha e deixa 0 ou 1 na pilha.

Dissecação

No pseudocódigo, é o seguinte:

p = 1
q = 0
foreach (i in input):
  q += i
  if (--p <= 0):      # in practice, if (--p == 0):
      p, q = q, p
return (p | q) == 0   # i.e. p == 0 && q == 0

qacumula a soma dos rótulos dos nós no nível atual da árvore; pfaz a contagem regressiva dos nós restantes no nível atual.

Peter Taylor
fonte
{X0@{+\(_{\}&}/|!}Eu acho que?
Martin Ender
Também parece que você deve salvar um byte usando um programa completo para evitar o @.
Martin Ender
1

Labirinto , 17 bytes

(
+?
;-)
,_"
@@,!

Experimente online!

A saída de verdade é -1e a saída falsa está vazia. Definir verdade e falsidade no Labirinto é um pouco complicado, porque os ramos do Labirinto são principalmente ternários. No entanto, a única maneira de construir uma condicional com duas ramificações confiáveis, você só pode fazer isso:

>"F
 T

Nesse caso, eu consideraria seguir em frente falsamente (porque a direção do movimento não é afetada) e virar verdade. Eles correspondem a zero e diferente de zero, respectivamente. O motivo pelo qual estou usando uma saída vazia para representar zero é que, se você canalizasse a saída de volta para outro programa Labyrinth, o operador de entrada ?enviaria um zero se a entrada estiver vazia, por isso considero válida a sequência vazia representação de zero.

Explicação

O algoritmo ainda é o mesmo que nas minhas respostas Mathematica e Retina, mas devido ao fluxo de controle do Labyrinth, funciona um pouco diferente desta vez:

  • Não trabalhamos com o contador total off-by-one aqui. Em vez disso, a) trabalhamos com um contador negativo eb) inicializamos -11inicialmente, para que desejemos que o contador seja negativo em toda a lista e atinja zero na última entrada. Isso realmente simplifica o fluxo de controle aqui.
  • Em vez de criar a lista completa e verificar se continha o valor errado, existem três condições de finalização possíveis:

    1. Atingimos o EOF antes de atingir uma contagem total de zero. Nesse caso, restam nós não utilizados e não imprimimos nada.
    2. Chegamos a zero e estamos na EOF. Nesse caso, temos uma árvore válida.
    3. Chegamos a zero e ainda não estamos na EOF. Nesse caso, ficamos sem nós antes de cobrir todos os elementos e não imprimimos nada.

Quanto ao código real, começamos no canto superior esquerdo. O (vira a zero, implícita no topo da pilha em um -1, que será a execução total. Em seguida, entramos no loop principal muito apertado do programa +?-)"_,;+:

+   Add the top two values. This does nothing on the first iteration,
    but gets rid of a helper-zero on subsequent iterations.
?   Read and push integer.
-   Subtract it from running total.
)   Increment.
"   No-op. There is a branch at this point. If the running total is zero,
    we move straight ahead onto the , (see below). Otherwise, the loop continues.
_   Push a zero. This is necessary to prevent the IP from turning south.
,   Read a character. This will either be the next separator (some positive
    number) or EOF (-1). If it's EOF, the IP turns south and the program
    terminates. Otherwise, the loop continues.
;   Discard the separator.

Isso deixa apenas os casos em que reduzimos o total de execução para zero em algum momento. O IP se move para o canto inferior direito ,e lê outro caractere para verificar se alcançamos o EOF. Caso contrário, o valor será positivo e o IP vira para oeste em direção ae @o programa termina. Se alcançamos o EOF, o IP vira para o leste e imprime o -1com !. O IP seguirá em direção à parte inferior esquerda @por um caminho um pouco estranho para finalizar o programa.

Martin Ender
fonte
0

Python, 82 bytes

lambda l:len(l)==sum(l)+1 and not any(list(l[x]>=len(l)-x for x in range(len(l))))

Precisa de mais casos de teste.

Sparr
fonte
Você não deve precisar de elenco com listse este é Python 2, pelo menos, e reorganizando e invertendo a segunda condição, você pode obtê-lo para 70 bytes:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade
^ Em relação a isso, você pode alterar o corpo do allser x<len(l)-y for y,x in enumerate(l)para salvar mais 2 bytes para obtê-lo a 68.
Kade
Não estou jogando golfe agora, porque não acho que seja uma solução precisa. Obrigado pelas dicas.
Sparr
0

Pitão, 13 bytes

qxsM._tMQ_1tl

Começamos calculando o preenchimento atual da árvore em todos os pontos da representação de entrada. Essa parte da ideia é amplamente emprestada de Martin Ender, então, graças a ele.sM._tMQ

Depois de termos essa lista, verificamos se o primeiro índice que contém -1( x..._1) é o comprimento da entrada menos um ( q...tl(Q)).

Não acredita que funciona? Tente você mesmo!

Steven H.
fonte