Ist es ein linearisierter Baum? (Erstausgabe)

11

Hintergrund

Ein unbeschrifteter Baum kann folgendermaßen aussehen:

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

Um diesen Baum zu linearisieren, kennzeichnen wir zuerst jeden Knoten omit der Anzahl der untergeordneten Knoten:

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

und schreiben Sie dann die Zahlen in einer Liste auf eine atemlose Weise, dh Zeile für Zeile und von links nach rechts:

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

Dies ist eine einzigartige und eindeutige Darstellung des obigen Baums, was bedeutet, dass keine zwei verschiedenen reinen Bäume die gleichen Linearisierungen haben und dass wir den ursprünglichen Baum aus der Liste rekonstruieren können.

Obwohl jeder Baum einer bestimmten Ganzzahlliste entspricht, stellt nicht jede Ganzzahlliste einen gültigen linearisierten Baum dar: Zum Beispiel [2, 0, 0, 0]stellt er keinen gültigen Baum dar. Wenn wir versuchen, ihn zu delinearisieren, erhalten wir diesen Baum

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

aber noch eine 0links in der Liste und nirgends zu setzen. Ebenso [2, 0]ist auch keine gültige Baumlinearisierung, da der de-linearisierte Baum einen leeren untergeordneten Punkt hat:

  2
 / \
0

Aufgabe

Entscheiden Sie anhand einer Ganzzahlliste, ob es sich um eine gültige Linearisierung eines Baums handelt, indem Sie so wenig Bytes wie möglich verwenden. Sie können ein vollständiges Programm oder eine Funktion schreiben.

Eingabe: Eine nicht leere Liste nicht negativer Ganzzahlen.

Ausgabe: Ein wahrer Wert, wenn die Liste eine Linearisierung eines Baums ist, andernfalls ein falscher Wert.

Testfälle

Wahrheit
[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]
Falsch
[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
quelle

Antworten:

4

Haskell, 44 Bytes

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

Definiert eine Funktion g, die eine Liste erstellt und einen Booleschen Wert zurückgibt. Sehen Sie, wie es alle Testfälle besteht .

Erläuterung

Dies beruht auf der Tatsache, dass Linearisierungen mit der Tiefe zuerst und der Breite zuerst die gleichen Arrays erzeugen. Siehe Martins Antworten für Details; Im Grunde geben beide die gleiche arithmetische Bedingung für das Array an.

Die Funktion ferhält die Eingabeliste, die in eine Singleton-Liste eingeschlossen ist. Es wird eine Nummer naus der Liste entfernt und ruft sich dann selbst nmal in der verbleibenden Liste auf, um die untergeordneten Elemente des gepoppten Knotens zu verarbeiten (Tiefe zuerst). Das Löschen der leeren Liste führt dazu [], was ich als Fehlerzustand verwende. Die Funktion gprüft, ob das Endergebnis [[]]der eindeutige, nicht fehlerhafte Zustand ohne unverarbeitete Knoten ist. Wenn Haskell schwach getippt wäre, könnte ich einfach 0oder etwas als Fehlerstatus verwenden und müsste die Eingabe nicht in eine andere Liste einschließen.

Zgarb
quelle
3

Mathematica, 38 Bytes

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

Die Grundidee ist, dass wir eine Reihe von zu füllenden Knoten verfolgen. Jedes Element in der Liste belegt einen Knoten und fügt so viele hinzu, wie es untergeordnete Elemente hat. Jedes Element iändert also die Gesamtzahl um i-1. Diese Zählung ist um eins verschoben, da sie von 1(der Wurzel) ausgehen sollte, nicht 0.

Damit der Baum gültig ist, können wir a) niemals in der 0gesamten Liste nach unten gehen , da wir den aktuellen Knoten nirgendwo platzieren müssten und b) -1am Ende enden müssen, sonst haben wir nicht verwendete Knoten übrig.

Wir erhalten diese laufende Summe der verbleibenden Knoten mit Accumulate[#-1](die die Präfixsummen der Eingabeliste minus eins berechnet). Und dann überprüfen wir, ob das letzte Element und nur das letzte Element -1mit:

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

Beachten Sie, dass die Überprüfung, ob das letzte Element negativ ist, ausreichend ist, da wir niemals um mehr als dekrementieren können. 1Wenn also die letzten Werte -2niedriger oder niedriger wären, wäre es unmöglich, dass die anderen Werte nicht negativ sind.

Martin Ender
quelle
2

Netzhaut , 29 Bytes

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

Probieren Sie es online aus! (Die erste Zeile aktiviert eine durch Zeilenvorschub getrennte Testsuite.)

Erläuterung

Die Grundidee ist die gleiche wie die meiner Mathematica-Antwort : Wir verfolgen die laufende Summe der verbleibenden Knoten und stellen sicher, dass sie niemals unter Null geht, sondern auf Null endet. Die Art und Weise, wie dies mit Regex implementiert wird, ist jedoch sehr unterschiedlich.

\d+
$*

Dadurch wird die Eingabe einfach in unär konvertiert und jede Ganzzahl nin n1s umgewandelt.

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

Hier geschieht die wahre Magie. Es ist eine ziemlich kurze Regex, die nur gültigen Bäumen entspricht, aber die Mechanik ist ziemlich subtil.

Ich verwende Ausgleichsgruppen , um die Anzahl der Knoten zu verfolgen. Dies ist eine Möglichkeit, mit Stapeln innerhalb des regulären Ausdrucks zu arbeiten.

Zunächst einmal kann ein solcher Stapel natürlich niemals eine negative Tiefe haben, so dass wir nicht wirklich eine Darstellung -1am Ende haben können, wie wir es in der Mathematica-Lösung tun. Allerdings können wir feststellen , dass das letzte Element des Eingangs hat Null auf einem gültigen Stapel sein (sonst könnten wir nicht mit am Ende -1). Es stellt sich heraus , dass es tatsächlich Bytes speichert prüfen beide , dass wir auf Null und mit Null verbleibenden Knoten enden.

Hier ist eine Aufschlüsselung des regulären Ausdrucks:

^        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
quelle
1

CJam (20 Bytes)

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

Online-Testsuite . Dies ist ein anonymer Block, der ein Array auf dem Stapel nimmt und 0 oder 1 auf dem Stapel belässt.

Präparation

Im Pseudocode ist dies:

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

qakkumuliert die Summe der Beschriftungen der Knoten auf der aktuellen Ebene im Baum; pzählt die in der aktuellen Ebene verbleibenden Knoten herunter.

Peter Taylor
quelle
{X0@{+\(_{\}&}/|!}Meiner Ansicht nach?
Martin Ender
Es scheint auch, dass Sie in der Lage sein sollten, ein Byte zu speichern, indem Sie ein vollständiges Programm verwenden, um das zu vermeiden @.
Martin Ender
1

Labyrinth , 17 Bytes

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

Probieren Sie es online aus!

Die Wahrheitsausgabe ist -1und die Falschausgabe ist leer. Die Definition von Wahrheit und Falschheit im Labyrinth ist etwas schwierig, da die Zweige des Labyrinths hauptsächlich ternär sind. Die einzige Möglichkeit, eine Bedingung mit zuverlässig zwei Zweigen zu erstellen, besteht jedoch nur darin:

>"F
 T

In diesem Fall würde ich in Betracht ziehen, mich geradeaus falsch zu bewegen (weil die Bewegungsrichtung nicht beeinflusst wird) und wahrheitsgemäß zu werden. Diese entsprechen Null bzw. Nicht-Null. Der Grund, warum ich eine leere Ausgabe verwende, um Null darzustellen, ist, dass der Eingabeoperator, wenn Sie die Ausgabe zurück in ein anderes Labyrinth-Programm leiten ?würden, tatsächlich eine Null drücken würde, wenn die Eingabe leer ist. Daher halte ich die leere Zeichenfolge für gültig Darstellung von Null.

Erläuterung

Der Algorithmus ist immer noch der gleiche wie in meinen Antworten von Mathematica und Retina, aber aufgrund des Kontrollflusses von Labyrinth funktioniert er diesmal etwas anders:

  • Wir arbeiten hier nicht mit dem Gesamtzähler nacheinander. Stattdessen arbeiten wir a) mit einem negativen Zähler und b) initialisieren ihn -11zunächst so, dass der Zähler in der gesamten Liste negativ sein soll und bei der letzten Eingabe Null drücken soll. Dies vereinfacht hier tatsächlich den Kontrollfluss.
  • Anstatt die vollständige Liste zu erstellen und zu überprüfen, ob sie den falschen Wert enthält, gibt es drei mögliche Beendigungsbedingungen:

    1. Wir haben EOF erreicht, bevor wir eine Gesamtzahl von Null erreicht haben. In diesem Fall sind noch nicht verwendete Knoten vorhanden und wir drucken nichts.
    2. Wir erreichen Null und sind bei EOF. In diesem Fall haben wir einen gültigen Baum.
    3. Wir erreichen Null und sind noch nicht bei EOF. In diesem Fall sind uns die Knoten ausgegangen, bevor wir alle Elemente abgedeckt haben, und wir drucken nichts.

Der eigentliche Code beginnt in der oberen linken Ecke. Das (schaltet die implizite Null auf der Oberseite des Stapels in einen -1, die die laufenden Summe wird. Wir betreten dann die sehr enge Hauptschleife des Programms +?-)"_,;+:

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

Das lässt nur die Fälle übrig, in denen wir die laufende Summe irgendwann auf Null reduziert haben. Die IP-Adresse bewegt sich nach rechts unten ,und liest ein anderes Zeichen, um zu überprüfen, ob wir EOF erreicht haben. Wenn nicht, ist der Wert positiv und die IP dreht sich nach Westen in Richtung @und das Programm wird beendet. Wenn wir EOF erreicht haben, dreht sich die IP nach Osten und druckt die -1mit !. Die IP wird sich dann @über einen etwas seltsamen Pfad nach links unten bewegen, um das Programm zu beenden.

Martin Ender
quelle
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))))

Benötigen Sie weitere Testfälle.

Sparr
quelle
Sie sollten nicht mit Casting arbeiten müssen, listwenn dies mindestens Python 2 ist. Wenn Sie die zweite Bedingung neu anordnen und invertieren, können Sie sie auf 70 Byte bringen:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade
^ In diesem Zusammenhang können Sie den Körper verändern allzu x<len(l)-y for y,x in enumerate(l)2 weiteren Bytes speichern Sie es auf 68 zu bekommen
Kade
Ich spiele momentan nicht weiter Golf, weil ich nicht denke, dass es eine genaue Lösung ist. Danke für die Tipps.
Sparr
0

Pyth, 13 Bytes

qxsM._tMQ_1tl

Wir beginnen mit der Berechnung der aktuellen Füllung des Baums an allen Punkten in der Eingabedarstellung. Dieser Teil der Idee ist größtenteils von Martin Ender entlehnt, also dank ihm.sM._tMQ

Sobald wir diese Liste haben, prüfen wir, ob der erste Index, der -1( x..._1) enthält, die Länge der Eingabe minus eins ( q...tl(Q)) ist.

Glauben Sie nicht, dass es funktioniert? Versuch es selber!

Steven H.
quelle