È un albero linearizzato? (Prima edizione)


11

sfondo

Un albero senza etichetta può apparire così:

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

Per linearizzare questo albero, etichettiamo prima ogni nodo ocon il suo numero di nodi figlio:

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

e quindi scrivere i numeri in un elenco in un modo mozzafiato, intendendo riga per riga e da sinistra a destra:

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

Questa è una rappresentazione unica e inequivocabile dell'albero sopra, nel senso che non ci sono due diversi alberi puri con le stesse linearizzazioni e che possiamo ricostruire l'albero originale dall'elenco.

Sebbene ogni albero corrisponda a un determinato elenco di numeri interi, non ogni elenco di numeri interi rappresenta un albero linearizzato valido: ad esempio [2, 0, 0, 0]non rappresenta un albero valido, se proviamo a de-linearizzarlo finiamo con questo albero

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

ma ho ancora una 0sinistra nell'elenco e nessun posto dove metterlo. Allo stesso modo [2, 0]non è neppure una linearizzazione dell'albero valida, poiché l'albero de-linearizzato ha un punto figlio vuoto:

  2
 / \
0

Compito

Dato un elenco di numeri interi, decidere se si tratta di una linearizzazione valida di un albero utilizzando il minor numero di byte possibile. È possibile scrivere un programma completo o una funzione.

Input: un elenco non vuoto di numeri interi non negativi.

Output: un valore di verità se l'elenco è una linearizzazione di un albero, altrimenti un valore di falsa.

Casi test

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]

Risposte:


4

Haskell, 44 byte

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

Definisce una funzione gche accetta un elenco e restituisce un valore booleano. Guardalo passare tutti i casi di test .

Spiegazione

Ciò si basa sul fatto che le linearizzazioni di profondità prima e ampiezza producono le stesse matrici. Vedi le risposte di Martin per i dettagli; fondamentalmente entrambi danno la stessa condizione aritmetica sull'array.

Alla funzione fviene fornito l'elenco di input racchiuso in un elenco singleton. Apre un numero ndall'elenco, quindi si chiama da solo nsull'elenco rimanente per elaborare i figli del nodo spuntato (prima la profondità). Si interrompe l'elenco vuoto [], che uso come stato di errore. La funzione gverifica che il risultato finale sia [[]]lo stato univoco non errato senza nodi non elaborati. Se Haskell fosse stato digitato debolmente, avrei potuto semplicemente usare 0qualcosa come stato di errore e non avrei dovuto inserire l'input in un altro elenco.


3

Mathematica, 38 byte

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

L'idea di base è che teniamo traccia di un numero di nodi da riempire. Ogni elemento nell'elenco utilizza un nodo e ne aggiunge quanti ne ha figli. Quindi ogni elemento icambia il conteggio totale di i-1. Questo conteggio è disattivato da uno, perché dovrebbe iniziare da 1(radice), non 0.

Perché l'albero sia valido noi a) non potremo mai andare sotto in 0tutta la lista, perché non avremmo alcun posto dove posizionare il nodo corrente eb) -1dovremmo finire con alla fine, altrimenti ci restano nodi inutilizzati.

Otteniamo questo totale parziale dei nodi rimanenti con Accumulate[#-1](che calcola le somme del prefisso dell'elenco di input meno uno). E poi controlliamo che l'ultimo elemento e solo l'ultimo elemento siano -1con:

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

Si noti che è sufficiente verificare che l'ultimo elemento sia negativo, poiché non si può mai diminuire di più di 1, quindi se gli ultimi valori erano -2o inferiori sarebbe impossibile che il minimo degli altri non sia negativo.


2

Retina , 29 byte

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

Provalo online! (La prima riga abilita una suite di test separata da avanzamento riga.)

Spiegazione

L'idea di base è la stessa della mia risposta Mathematica : teniamo traccia di un totale parziale di nodi rimanenti, assicurandoci che non scenda mai sotto lo zero ma finisca sullo zero. Tuttavia, il modo in cui questo viene implementato con regex è molto diverso.

\d+
$*

Questo semplicemente converte l'input in unario, trasformando ogni numero intero nin n1s.

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

Qui è dove accade la vera magia. È una regex piuttosto breve che corrisponde solo agli alberi validi, ma i suoi meccanismi sono abbastanza sottili.

Sto usando i gruppi di bilanciamento per tenere traccia del numero di nodi, che sono un modo per lavorare con stack all'interno del regex.

Prima di tutto, ovviamente un tale stack non può mai avere una profondità negativa, quindi non possiamo davvero finire con una rappresentazione -1alla fine, come facciamo nella soluzione Mathematica. Tuttavia, possiamo notare che l'elemento finale dell'input deve essere zero su uno stack valido (altrimenti non potremmo finire con -1). Si scopre che in realtà salva i byte per verificare sia che finiamo su zero che con zero nodi rimanenti.

Quindi ecco una ripartizione della 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.

1

CJam (20 byte)

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

Suite di test online . Questo è un blocco anonimo che accetta un array nello stack e lascia 0 o 1 nello stack.

Dissezione

In pseudocodice questo è:

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

qaccumula la somma delle etichette dei nodi al livello corrente nella struttura; pconta i nodi rimanenti nel livello corrente.


{X0@{+\(_{\}&}/|!}Credo?
Martin Ender,

Inoltre sembra che dovresti essere in grado di salvare un byte utilizzando un programma completo per evitare il @.
Martin Ender,

1

Labirinto , 17 byte

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

Provalo online!

L'output vero è -1e l'output falso è vuoto. Definire la verità e la falsità in Labyrinth è un po 'complicato, perché i rami di Labyrinth sono principalmente ternari. Tuttavia, l'unico modo per costruire un condizionale con due rami in modo affidabile, puoi solo fare questo:

>"F
 T

In questo caso, prenderei in considerazione l'idea di spostarmi dritto in avanti (perché la direzione del movimento è inalterata) e trasformarmi in verità. Questi corrispondono rispettivamente a zero e non zero. Il motivo per cui sto usando un output vuoto per rappresentare zero è che, se dovessi reindirizzare l'output in un altro programma Labyrinth, l'operatore di input ?spingerebbe effettivamente uno zero se l'input è vuoto, quindi considero valida la stringa vuota rappresentazione di zero.

Spiegazione

L'algoritmo è sempre lo stesso delle risposte Mathematica e Retina, ma a causa del flusso di controllo di Labyrinth, questa volta funziona un po 'diverso:

  • Non lavoriamo con il contatore totale off-by-one qui. Invece a) lavoriamo con un contatore negativo eb) inizializziamo -11inizialmente, in modo che vogliamo che il contatore sia negativo in tutta la lista e colpiamo zero sull'ultimo input. Questo in realtà semplifica il flusso di controllo qui.
  • Invece di creare l'elenco completo e verificare se conteneva il valore errato, ci sono tre possibili condizioni di terminazione:

    1. Colpiamo EOF prima di raggiungere un conteggio totale di zero. In tal caso, rimangono nodi inutilizzati e non viene stampato nulla.
    2. Raggiungiamo lo zero e siamo a EOF. In questo caso, abbiamo un albero valido.
    3. Raggiungiamo lo zero e non siamo ancora a EOF. In questo caso, abbiamo esaurito i nodi prima di coprire tutti gli elementi e non stampiamo nulla.

Per quanto riguarda il codice effettivo, iniziamo nell'angolo in alto a sinistra. Il valore (trasforma lo zero implicito in cima allo stack in a -1, che sarà il totale parziale. Entriamo quindi nel loop principale molto stretto del programma +?-)"_,;+,:

+   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.

Ciò lascia solo i casi in cui a un certo punto abbiamo ridotto il totale parziale a zero. L'IP si sposta in basso a destra ,e legge un altro personaggio per verificare se abbiamo raggiunto EOF. In caso contrario, il valore sarà positivo e l'IP gira verso ovest verso @e il programma termina. Se abbiamo raggiunto EOF, l'IP gira verso est e stampa il -1con !. L'IP si aprirà quindi verso l'angolo in basso a sinistra @attraverso un percorso leggermente strano per terminare il programma.


0

Python, 82 byte

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

Hai bisogno di più casi di test.


Non dovresti aver bisogno di eseguire il cast listse almeno questo è Python 2, e riordinando e invertendo la seconda condizione puoi portarlo a 70 byte:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade,

^ In relazione a questo, puoi cambiare il corpo di allessere x<len(l)-y for y,x in enumerate(l)per salvare altri 2 byte per portarlo a 68.
Kade

In questo momento non sto giocando a golf ulteriormente perché non penso che sia una soluzione accurata. Grazie per i suggerimenti
Sparr,

0

Pyth, 13 byte

qxsM._tMQ_1tl

Iniziamo calcolando l'attuale riempimento dell'albero in tutti i punti della rappresentazione di input. Quella parte dell'idea è in gran parte presa in prestito da Martin Ender, quindi grazie a lui.sM._tMQ

Una volta che abbiamo questo elenco, controlliamo se il primo indice contenente -1( x..._1) è la lunghezza dell'input meno uno ( q...tl(Q)).

Non ci credi che funzioni? Provate voi stessi!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.