Це лінійне дерево? (Перше видання)


11

Фон

Немечене дерево може виглядати так:

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

Щоб лінеалізувати це дерево, спочатку позначимо кожен вузол oйого кількістю дочірніх вузлів:

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

а потім запишіть номери в список, дихаючи першим способом, означаючи рядок за рядком і зліва направо:

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

Це унікальне та однозначне подання дерева вище, що означає, що жодне два різних чистих дерева не матиме однакових лінеаризацій і що ми можемо реконструювати оригінальне дерево зі списку.

Хоча кожне дерево відповідає певному цілому списку, не кожен цілий список представляє дійсне лінеаризоване дерево: Наприклад [2, 0, 0, 0], не представляє дійсного дерева, якщо ми намагаємося делінеаризувати його, ми закінчуємо цим деревом

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

але все ж 0у списку ліворуч і ніде його не помістити. Так само [2, 0]не є дійсною лінеаризацією дерев, оскільки делінеаризоване дерево має порожнє дочірнє місце:

  2
 / \
0

Завдання

Давши цілий список, вирішіть, чи це дійсна лінеаризація дерева, використовуючи якомога менше байтів. Ви можете написати повну програму або функцію.

Введення: Непорожній список невід’ємних цілих чисел.

Вихідні дані: значення "truthy", якщо список є лінеаризацією дерева, фальшиве значення в іншому випадку.

Тестові шафи

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]
Фальсі
[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]

Відповіді:


4

Haskell, 44 байти

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

Визначає функцію, gяка приймає список і повертає булева. Дивіться, проходять усі тестові справи .

Пояснення

Це спирається на той факт, що лінеаризація на глибині першої та першої ширини виробляє однакові масиви. Деталі див. У відповідях Мартіна; в основному вони обидва дають однакову арифметичну умову на масиві.

Функція fотримує список вхідних даних, загорнутий у однотонний список. Він вискакує одне число nзі списку, а потім викликає себе в іншому nсписку, щоб обробити дітей спливаючого вузла (глибина спочатку). З'явлення порожнього списку призводить до того [], що я використовую як стан помилки. Функція gперевіряє, що є кінцевим результатом [[]], унікальний не помилковий стан без неперероблених вузлів. Якщо Haskell був слабо набраний, я міг би просто використовувати 0або щось як стан помилки, і мені не доведеться переносити дані в інший список.


3

Математика, 38 байт

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

Основна ідея полягає в тому, щоб ми відстежували кількість вузлів, які потрібно заповнити. Кожен елемент списку використовує один вузол і додає стільки, скільки має дітей. Таким чином, кожен елемент iзмінює загальну кількість на i-1. Цей підрахунок вимикається на одне, бо слід починати з 1(кореня), а не 0.

Для того, щоб дерево було дійсним, ми а) ніколи не можемо пройти нижче 0у списку, тому що нам не було б де розмістити поточний вузол, а б) потрібно закінчити -1в кінці, інакше у нас залишилися невикористані вузли.

Ми отримуємо цей запущений загальний обсяг решти вузлів з Accumulate[#-1](який обчислює префіксні суми вхідного списку мінус один). А потім перевіряємо, що останній елемент і лише останній елемент -1:

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

Зауважте, що перевірка того, що останній елемент є негативною, є достатньою, оскільки ми ніколи не можемо зменшити декремент на більш ніж 1, тому, якщо останні значення були -2або нижчі, неможливо, щоб мінімум інших був негативним.


2

Сітківка , 29 байт

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

Спробуйте в Інтернеті! (Перший рядок включає тестовий набір, відокремлений виведенням ліній.)

Пояснення

Основна ідея така ж, як і в моїй відповіді Mathematica : ми відслідковуємо загальну кількість запущених вузлів, гарантуємо, що вона ніколи не виходить нижче нуля, а закінчується на нулі. Однак спосіб цього реалізується за допомогою регулярного вираження дуже різний.

\d+
$*

Це просто перетворює вхід в одинаковий, перетворюючи кожне ціле число nна n1s.

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

Тут відбувається справжня магія. Це досить короткий регулярний вираз, який відповідає лише діючим деревам, але це механіка досить тонка.

Я використовую балансуючі групи для відстеження кількості вузлів, які є способом роботи зі стеками всередині регулярного вираження.

По-перше, звичайно, такий стек ніколи не може мати негативну глибину, тому ми не можемо насправді закінчити представлення -1в кінці, як це робимо в рішенні Mathematica. Тим НЕ менше, ми можемо відзначити , що кінцевий елемент введення повинен бути нуль на дійсну стеку ( в іншому випадку ми не могли закінчити з -1). Виявляється, він насправді економить байти, щоб перевірити як ми закінчуємо нуль, так і нуль, що залишився.

Тож ось розбивка регулярного вираження:

^        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 байт)

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

Інтернет-тестовий набір . Це анонімний блок, який займає масив на стеку і залишає 0 або 1 на стеку.

Розсічення

У псевдокоді це:

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

qакумулює суму міток вузлів на поточному рівні в дереві; pвідлічує вузли, що залишилися на поточному рівні.


{X0@{+\(_{\}&}/|!}Я думаю?
Мартін Ендер

Також здається, що ви повинні мати можливість зберегти байт, використовуючи повну програму, щоб уникнути @.
Мартін Ендер

1

Лабіринт , 17 байт

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

Спробуйте в Інтернеті!

Поточний вихід є, -1а помилковий вихід порожній. Визначення правди і хибності в Лабіринті дещо складне, тому що гілки Лабіринту в основному потрійні. Однак єдиний спосіб побудувати умовний з надійно двома гілками, ви можете лише це зробити:

>"F
 T

У цьому випадку я б подумав про те, щоб рухатись прямо вперед фальшивим (оскільки напрямок руху не впливає) та повертати на правду. Вони відповідають нулю і не нулю відповідно. Причиною того, що я використовую порожній висновок для представлення нуля, є те, що якби ви передавали висновок назад в іншу програму Labyrinth, оператор введення ?насправді натисне нуль, якщо вхід порожній, тому я вважаю порожню рядок дійсною подання нуля.

Пояснення

Алгоритм все ще такий самий, як у моїх відповідях Mathematica та Retina, але завдяки потоку управління Лабіринту цього разу він працює трохи інакше:

  • Тут ми не працюємо з загальним лічильником без набору. Натомість ми a) працюємо з негативним лічильником і b) ініціалізуємо його на -11початковому рівні, щоб ми хотіли, щоб лічильник був негативним у всьому списку і на останньому введенні потрапив до нуля. Це фактично спрощує контрольний потік тут.
  • Замість складання повного списку та перевірки, чи містив він неправильне значення, існують три можливі умови припинення:

    1. Ми потрапили на EOF, перш ніж досягти загального рахунку нуля. У цьому випадку залишилися невикористані вузли, і ми нічого не друкуємо.
    2. Ми досягаємо нуля і ми на EOF. У цьому випадку у нас є дійсне дерево.
    3. Ми досягаємо нуля і ще не в EOF. У цьому випадку нам не вистачало вузлів, перш ніж покрити всі елементи, і ми нічого не друкуємо.

Що стосується власне коду, то ми починаємо у верхньому лівому куті. (Перетворює неявний нуль на вершині стека в -1, який буде наростаючим підсумком. Потім ми входимо в дуже тугий основний цикл програми +?-)"_,;+:

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

Це залишає лише випадки, коли ми в певний момент зменшили загальний обсяг до нуля. IP-адреса рухається в нижньому правому куті ,і читає інший символ, щоб перевірити, чи досягли ми EOF. Якщо ні, значення буде позитивним, і IP повернеться на захід у напрямку, @а програма припиниться. Якщо ми дійдемо до EOF, IP повертає на схід і друкує -1с !. Тоді IP буде проходити вниз ліворуч @через трохи дивний шлях для припинення програми.


0

Пітон, 82 байти

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

Потрібно більше тестових випадків.


Вам не потрібно брати участь у тому, listякщо це принаймні Python 2, і переставляючи та інвертуючи другу умову, ви можете отримати його до 70 байт:lambda l:all(l[x]<len(l)-x for x in range(len(l)))and len(l)==sum(l)+1
Kade

^ У зв'язку з цим, ви можете змінити тіло allна, x<len(l)-y for y,x in enumerate(l)щоб зберегти ще 2 байти, щоб отримати його до 68.
Каде

Я зараз не займаюся цим гольфом, тому що не думаю, що це точне рішення. Дякую за поради.
Спарр

0

Pyth, 13 байт

qxsM._tMQ_1tl

Почнемо з обчислення поточної заповненості дерева у всіх точках вхідного подання. Ця частина ідеї багато в чому запозичена у Мартіна Ендера, тому завдяки йому.sM._tMQ

Після того як у нас є цей список, ми перевіряємо, чи є перший індекс, що містить -1( x..._1) - довжина введення мінус один ( q...tl(Q)).

Не вірите, що це працює? Спробуйте самі!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.