Перерахуйте двійкові дерева


20

Двійкові дерева

Бінарне дерево - це дерево з вузлами трьох типів:

  • кінцеві вузли, у яких немає дітей
  • одинарні вузли, у яких одна дитина
  • двійкові вузли, у яких по двоє дітей

Ми можемо представити їх наступною граматикою, наведеною у BNF (форма Backus – Naur):

<e> ::= 
      <terminal>   
    | <unary>
    | <binary>

<terminal> ::= 
    "0"

<unary> ::= 
    "(1" <e> ")"

<binary> ::= 
    "(2" <e> " " <e> ")"

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

Числа Моцкіна

Числа Моцкіна ( OEIS ) ( Вікіпедія ) мають багато тлумачень, але одна інтерпретація полягає в тому, що nчисло Моцкіна - це кількість різних двійкових дерев з nвузлами. Починається таблиця чисел Моцкіна

N          Motzkin number M(N)
1          1
2          1
3          2 
4          4 
5          9 
6         21 
7         51 
8        127 
    ...

наприклад, M(5)це 9, а дев'ять різних двійкових дерев з 5 вузлами є

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)  

Завдання

Візьміть одне додатне ціле число nяк вхід і вивід усіх окремих двійкових дерев з nвузлами.

Приклади nвід 1 до 5 з дужками, включені для читабельності

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)

Вхідні дані

Вхід буде одним натуральним числом.

Вихідні дані

Вихід повинен бути зрозумілим поданням різних бінарних дерев із такою кількістю вузлів. Не обов'язково використовувати точний рядок, наведений вище в граматиці BNF: достатньо, щоб використовуваний синтаксис давав однозначне зображення дерев. Наприклад , ви могли б використовувати []замість (), додатковий рівень дужок [[]]замість [], зовнішньої дужки присутні або відсутні, додаткові коми або без ком, зайві прогалини, дужки чи ні дужка і т.д.

Все це рівнозначно:

(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

Також варіант, запропонований @xnor у коментарі. Оскільки існує спосіб перекласти це у формат, який можна зрозуміти, він прийнятний.

[[[]][]]  is (2 (1 0) 0)

Щоб зробити це легше зрозуміти , перетворити деякі з []в ()подобається так

[([])()]

Тепер, якщо ви почнете з

[]

потім вставити двійковий файл, який потребує двох отриманих виразів

 [()()] which is 2

а потім для першого () вставити унар, якому потрібен один вираз, який ви отримаєте

 [([])()] which is 21

але так []чи ()без внутрішнього брекета може представляти 0 , який не потребує більше виразів ви можете інтерпретувати його як

 2100

Зауважте, що відповіді повинні теоретично працювати з нескінченною пам'яттю, але, очевидно, не вистачить пам'яті для обмеженого введення, що залежить від реалізації.

Варіації випуску

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)  

Можливе місце для перевірки дублікатів дерев

Одне місце для перевірки дубліката - за допомогою М (5).
Це одне дерево було сформовано двічі для M (5) з M (4) дерев

(2 (1 0) (1 0))  

Перший додавши одинарну гілку до

(2 (1 0) 0)

по-друге, додавши одинарну гілку до

(2 0 (1 0))

Розуміння BNF

BNF складається з простих правил:

<symbol> ::= expression

де зліва - назва символу в оточенні <>.
Праворуч - вираз для побудови символу. Деякі правила використовують інші правила в побудові, наприклад

<e> ::= <terminal>

e може бути terminal

а деякі правила містять символи, які використовуються при побудові символу, наприклад

<terminal> ::= "0"

terminal - це лише нульовий символ.

Деякі правила мають кілька способів їх побудови, наприклад

<e> ::= 
      <terminal>   
    | <unary>
    | <binary>

А eможе бути a <terminal>або a <unary>або a <binary>.

А деякі правила - це послідовність частин, наприклад

<unary> ::= "(1" <e> ")"

unaryЦе символи (1слід , що можна побудувати для eнаступних ).

Ви завжди починаєте зі стартового правила, яке для цього потрібно <e>.

Деякі прості приклади:

Найпростіша послідовність - справедлива 0. Тож ми починаємо із стартового правила <e>і бачимо, що є три варіанти:

  <terminal>   
| <unary>
| <binary>

тому візьміть перший <terminal>. Тепер термінал не має вибору і є 0. Так замінити <terminal>з 0в <e>правилах , і ви зробили.

Тоді наступний (1 0). Почніть з <e>та використовуйте правило, <unary>яке має

"(1" <e> ")"

Тепер це потрібно, <e>тому ми повертаємось до <e>та робимо вибір одного з трьох, цього разу вибираючи, <terminal>що дає 0. Заміна 0на (1 <e> )дає (1 0), і це замінюється на <unary>так <e>є (1 0).


Отже, двійкове дерево? "бінарне дерево - це структура даних про дерево, в якій кожен вузол має щонайбільше двох дітей"
fəˈnɛtɪk

3
Ваш опис - опис бінарного дерева. Бінарні дерева не повинні мати 2 дітей. Це просто означає, що вони мають максимум 2 дітей. Я думаю, що унар-бінарний - це лише більш конкретний термін, який насправді не означає нічого іншого.
fəˈnɛtɪk

Поміркуйте, що таке "BNF" для тих, хто не є інформатиками
Луїс Мендо

1
@GuyCoder Моя думка, якщо хтось бачить "BNF" і не знає, що це означає, вони можуть бути відкладені і перестати читати. Можливо, використання імені замість абревіатури та додавання посилання на Вікіпедію буде досить
Луїс Мендо

4
@ mbomb007 Ім'я змінено. Я маю за це отримати нагороду з рівного тиску. :)
Гай Кодер

Відповіді:


12

Хаскелл, 68 байт

t 0=[""]
t 1=["0"]
t n=['(':x++y++")"|k<-[1..n-1],x<-t k,y<-t$n-k-1]

Кінцеві вузли представлені 0унарними та двійковими вузлами (e)відповідно. (ee), тож два тривузлові дерева задаються як (00)і ((0)).

Приклади:

*Main> t 5
["(0(00))","(0((0)))","((0)(0))","((00)0)","(((0))0)","((0(0)))","(((0)0))","(((00)))","((((0))))"]
*Main> length $ t 8
127
*Main> length $ t 15
113634 


5

Pyth ( 24 21 19 байт)

Це засновано на моєму рішенні Python 3 .

f!|sTf<sY0._T^}1_1t

Вперше я використовую Pyth, тому це, ймовірно, все ще пограє.

Приклад , вихід при введенні 4:

[[1, 0, -1], [1, -1, 0], [0, 1, -1], [0, 0, 0]]

1 являє собою двійковий вузол, 0 являє собою одинарний вузол, а -1 являє собою кінцевий вузол. В кінці кожного дерева є мається на увазі кінцевий вузол.

Пояснення :

f!|sTf<sY0._T^}1_1t
f                    filter
             ^    t  length n-1 lists of elements
              }1_1   from [1, 0, -1]
 !|                  for when both
   sT                sum of list is 0, and
     f    ._T        for each prefix of list,
      <sY0           sum of prefix is non-negative.


4

мозковий ебать, 107 байт

,>++>-[-[<-[<-[>>[[>+<-]<]>+>[[<+>>>>>+<<<<-]>]>>++>,++++>]>[<+>-[+>>]>[<->[.<<<
<<]+[->+]+>>>]]]]<[[,<]<]<]

Відформатовано:

,>++>-
[
  -
  [
    <-
    [
      <-
      [
        >>
        [[>+<-]<]
        >+>
        [[<+> >>>>+<<<<-]>]
        >>++>,++++>
      ]
      >
      [
        <+>-
        [
          +>>
        ]
        >
        [
          <->[.<<<<<]
          +[->+]
          +>>>
        ]
      ]
    ]
  ]
  <
  [
    [,<]
    <
  ]
  <
]

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

Введення приймається як байт , а дерево 12100подається як \x01\x02\x03\x02: перетворити назад, перекласти tr/\x01\x02\x03/012/, повернути рядок і додати остаточний 0. Дерева розділені на \xfe. (Вихід може бути полегшений для читання, наприклад, змінивши перший -на -36і .на +47.-47, де -36означає рядок з 36 -символів тощо)

Цей підхід використовує властивість (яку також використовував Бен Франкель): розглядаючи можливі вузли як -1, 0, 1і нехтуючи кінцевим -1вузлом, список представляє дійсне дерево, якщо і лише якщо (1) всі префікси списку мають негативну суму, та (2) сума всього списку дорівнює0 . Перша умова підтримується під час генерації проміжних вузлів, тому лише наприкінці потрібно перевірити в кінці.

Стрічка ділиться на 5-вузлові клітини,

i d x 0 0

де iіндекс (спадаючий зліва направо), dє частковою сумою і xє елементом.

Ескіз контрольного потоку:

take n and push initial node
while stack is non-empty:
    if rightmost node can be decremented:
        decrement rightmost node
        if there are less than n nodes:
            push new node
        else if valid tree:
            print
    else:
        backtrack (pop)

Зауважте, що інколи значення зберігається або ініціалізується як одне або два більше, ніж фактичне (концептуальне) значення, і коригується за потребою.


3

Python 3 ( 138 134 128 121 119 байт)

from itertools import*
lambda n:[any(sum(t[:k])<0for k in range(n))|sum(t)or print(t)for t in product(*[[-1,0,1]]*~-n)]

Приклад виводу для n=5:

(0, 0, 0, 0)
(0, 0, 1, -1)
(0, 1, -1, 0)
(0, 1, 0, -1)
(1, -1, 0, 0)
(1, -1, 1, -1)
(1, 0, -1, 0)
(1, 0, 0, -1)
(1, 1, -1, -1)

1 являє собою двійковий вузол, 0 являє собою одинарний вузол, а -1 являє собою кінцевий вузол. В кінці кожного дерева є мається на увазі кінцевий вузол.

Програма починає займати занадто довго навколо n=17.


3

JavaScript (Firefox 30-57), 79 байт

f=(m,l=0)=>m?[for(n of[1,0,-1])if(l>n&l<=m+n)for(a of f(m-1,l-n))[...a,n]]:[[]]

Де -1являє собою термінал, 0одинарний вузол і 1двійковий вузол. Починає повільно m=14на моєму ПК. Рекурсивно працює від кінця дерева.

  • Кількість незареєстрованих вузлів lобмежується тим, що в кінці може бути лише 1 вузол.
  • Тип наступного вузла nобмежений необхідністю мати достатньо необлікуваних вузлів, щоб бути його дітьми.

2

Пролог, 149 144 138 137 131 107 байт

e(L,L)-->[0].

e([_|A],L)--> 
    [1],
    e(A,L).

e([_,_|A],L)--> 
    [2],
    e(A,B), 
    e(B,L).

e(M,E):-                   
    length([_|L],M),        
    e(L,[],E,[]).           

?- e(5,S).
S = [1, 1, 1, 1, 0] ;
S = [1, 1, 2, 0, 0] ;
S = [1, 2, 0, 1, 0] ;
S = [1, 2, 1, 0, 0] ;
S = [2, 0, 1, 1, 0] ;
S = [2, 0, 2, 0, 0] ;
S = [2, 1, 0, 1, 0] ;
S = [2, 1, 1, 0, 0] ;
S = [2, 2, 0, 0, 0].

І порахувати рішення

e_count(N,Count) :-
    length([_|Ls], N),
    findall(., phrase(e(Ls,[]),E), Sols),
    length(Sols, Count).

?- e_count(N,Count).
N = Count, Count = 1 ;
N = 2, Count = 1 ;
N = 3, Count = 2 ;
N = Count, Count = 4 ;
N = 5, Count = 9 ;
N = 6, Count = 21 ;
N = 7, Count = 51 ;
N = 8, Count = 127 ;
N = 9, Count = 323 ;
N = 10, Count = 835 ;
N = 11, Count = 2188 ;
N = 12, Count = 5798 ;
N = 13, Count = 15511 ;
N = 14, Count = 41835 ;
N = 15, Count = 113634 

1

Пітон, 71 байт

f=lambda n:{(a+b,)for k in range(n)for a in f(k)for b in f(n+~k)}or[()]

Це являє собою дерева як такі вкладені кортежі, як такі ((((),), ()),), які можна перетворити на ((())())вилучення комами, пробілами та зовнішніми ().

Більш рання 76-байтна версія:

f=lambda n:{'('+a+b+')'for k in range(n)for a in f(k)for b in f(n+~k)}or['']

1

CJam, 38 байт

Використовується інший підхід, на який відповідає CJam Пітера Тейлора.

3rim*{:(1\+[{1$+}*])\:(_:z#|!},

Вихід буде чимось на зразок 1110120020102100. Кожне дерево - це група nцифр (де nвводиться число).

Основна ідея полягає в тому, що ми створюємо кожен можливий рядок цифр 0, 1і 2, а потім фільтруємо лише ті, які є добре сформованими деревами.

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