İkili ağaçları numaralandırma


20

İkili ağaçlar

İkili ağaç, üç tip düğümü olan bir ağaçtır:

  • çocuğu olmayan terminal düğümleri
  • her biri bir çocuğu olan tekli düğümler
  • her biri iki çocuğu olan ikili düğümler

Bunları BNF'de (Backus-Naur formu) verilen aşağıdaki dilbilgisi ile temsil edebiliriz :

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

<terminal> ::= 
    "0"

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

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

Bu dilbilgisinde düğümler ön siparişte verilir ve her bir düğüm, sahip olduğu çocuk sayısı olan bir rakamla temsil edilir.

Motzkin numaraları

Motzkin numaralar ( OEIS ) ( Vikipedi ) bir çok yorumlar var, ama bir yorumlama olmasıdır nMotzkin numarası inci ile ayrı ikili ağaçların sayısıdır ndüğümler. Motzkin sayıları tablosu başlar

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

örneğin M(5)9'dur ve 5 düğümü olan dokuz ayrı ikili ağaç

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)  

Görev

nGiriş olarak tek bir pozitif tamsayı alın ve ndüğümlerle birlikte tüm ikili ağaçların çıktısını alın .

nOkunabilirlik için parantez içeren 1'den 5'e kadar örnekler

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)

Giriş

Giriş bir pozitif tamsayı olacaktır.

Çıktı

Çıktı, bu çok sayıda düğümle ayrı ikili ağaçların anlaşılır bir temsili olmalıdır. Yukarıdaki BNF dilbilgisi tarafından verilen dizgenin tam olarak kullanılması zorunlu değildir: kullanılan sözdiziminin ağaçların açık bir temsilini vermesi yeterlidir. Örneğin Kullanabileceğin []yerine ()parantez ekstra düzeyi [[]]yerine [], dış parantez vb ekstra virgül veya hiç virgül, fazladan boşluk, parantez veya hiç parantez, mevcut veya eksik

Bunların hepsi eşdeğerdir:

(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

Ayrıca bir yorumda @xnor tarafından amaçlanan bir varyasyon. Bunu anlaşılabilecek bir formata çevirmenin bir yolu olduğundan kabul edilebilir.

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

Kolay bu bazılarını dönüştürmek anlamalarını sağlamak []için ()bu yüzden mi

[([])()]

Şimdi ile başlarsanız

[]

sonra iki ifadeye ihtiyaç duyan bir ikili kod ekleyin

 [()()] which is 2

ve sonra ilk () için bir ifadeye ihtiyaç duyan bir tekli

 [([])()] which is 21

ancak iç parantez içermeyen []veya ()içermeyen, daha fazla ifade gerektirmeyen 0'ı temsil edebildiğinden, bunu şu şekilde yorumlayabilirsiniz:

 2100

Yanıtların teorik olarak sonsuz bellekle çalışması gerektiğini, ancak uygulamaya bağlı sonlu giriş için bellekte yer kalmayacağını unutmayın.

Çıktı varyasyonları

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)  

Yinelenen ağaçları kontrol etmek için olası bir yer

Kopyaları kontrol etmek için bir yer M (5) 'dir.
Bu bir ağaç M (4) ağaçlarından M (5) için iki kez üretildi

(2 (1 0) (1 0))  

ilki bir tekli dal ekleyerek

(2 (1 0) 0)

ve ikincisi,

(2 0 (1 0))

BNF'yi anlama

BNF basit kurallardan oluşur:

<symbol> ::= expression

solda etrafı çevrili bir sembol adıdır <>.
Sağda, sembolü oluşturmak için kullanılan ifade bulunur. Bazı kurallar yapıdaki diğer kuralları kullanır, örn.

<e> ::= <terminal>

e Olabilir terminal

ve bazı kurallarda sembolün oluşturulmasında kullanılan karakterler vardır, ör.

<terminal> ::= "0"

terminal sadece sıfır karakteri.

Bazı kuralların bunları yapılandırmanın birden fazla yolu vardır, ör.

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

A e, a <terminal>veya a <unary>veya a olabilir <binary>.

Ve bazı kurallar bir parça dizisidir, ör.

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

Bir unarykarakterdir (1için inşa edilebilir ne izledi eizledi ).

Bunun için her zaman başlangıç ​​kuralıyla başlarsınız <e>.

Bazı basit örnekler:

En basit sekans sadece 0. Bu yüzden başlangıç ​​kuralıyla başlıyoruz <e>ve üç seçenek olduğunu görüyoruz:

  <terminal>   
| <unary>
| <binary>

ilkini al <terminal>. Artık bir terminalin seçeneği yok ve öyle 0. Yani yerini <terminal>ile 0de <e>kural ve bitirdiniz.

Sonra bir sonraki (1 0). İle başlayın <e>ve kullanım kuralı <unary>vardır

"(1" <e> ")"

Şimdi bunun bir ihtiyacı var, <e>bu yüzden geri dönüyoruz <e>ve bu sefer seçen üç taneden birini <terminal>seçiyoruz 0. Değiştirme 0içine (1 <e> )verir (1 0)ve içine bu değiştirilir <unary>böylece <e>olduğunu (1 0).


Yani, ikili bir ağaç mı? "İkili ağaç, her düğümün en fazla iki çocuğu olan bir ağaç veri yapısıdır"
fəˈnɛtɪk

3
Açıklamanız ikili ağaçtır. İkili ağaçların 2 çocuğu olması gerekmez. Bu sadece en fazla 2 çocuğu olduğu anlamına gelir. Sanırım tek-ikili, daha farklı bir şey anlamına gelmeyen daha spesifik bir terimdir.
fəˈnɛtɪk

Bilgisayar bilimcisi olmayan bizler için "BNF" nin ne olduğunu açıklamayı düşünün
Luis Mendo

1
@GuyCoder Demek istediğim, eğer birisi "BNF" görürse ve bunun ne anlama gelebileceğini bilmiyorsa ve okumayı bırakabilir. Belki de kısaltma yerine ismi kullanmak ve Wikipedia'ya bir bağlantı eklemek yeterli olacaktır
Luis Mendo

4
@ mbomb007 İsim değişti. Bunun için bir akran baskısı ödülü almalıyım. :)
Guy Coder

Yanıtlar:


12

Haskell, 68 bayt

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

Terminal düğümleri 0, tekli ve ikili düğümlerle sırasıyla temsil edilir (e). (ee)Bu yüzden, iki, üç düğüm ağaç olarak verilmiştir (00)ve ((0)).

Örnekler:

*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

CJam (37 bayt)

0aa{_2m*2\f+1Y$f+++:e__&}qi:A*{,A=},p

Çevrimiçi demo . Bunun çok verimli olmadığını ve muhtemelen 5çevrimiçi giriş hesaplamayı denemek istemediğinizi unutmayın .

Takip diseksiyonu.


5

Pyth ( 24 21 19 bayt)

Bu benim Python 3 çözümüme dayanıyor .

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

Pyth'i ilk kez kullanıyorum, bu yüzden hala golf oynamak mümkün.

Örnek , giriş aşağıdaki gibi olduğunda 4:

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

1 bir ikili düğümü, 0 bir tekli düğümü ve -1 bir terminal düğümünü temsil eder. Her ağacın sonunda zımni bir terminal düğümü vardır.

Açıklama :

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.

İlgilendikleriniz: Pyth kaynak kodu
Guy Coder

4

brainfuck, 107 bayt

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

biçimlendirilmiş:

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

Çevrimiçi deneyin

Giriş bir bayt olarak alınır ve ağaç 12100şu şekilde temsil edilir \x01\x02\x03\x02: geri dönüştürmek tr/\x01\x02\x03/012/, çevirmek, dizeyi ters çevirmek ve bir final eklemek için 0. Ağaçlar birbirinden ayrılır \xfe. (Çıkış örn birinci değiştirerek daha kolay okunur hale getirilebilir -içine -36ve .içine +47.-47, -3636 bir dizi aracı -karakterler, vb)

Bu yaklaşım (Ben Frankel'in de kullandığı) mülkten yararlanır: olası düğümleri göz önünde bulundurarak -1, 0, 1ve son -1düğümü göz ardı ederek , bir liste yalnızca (1) tüm öneklerin negatif olmayan toplamı varsa, ve (2) tüm listenin toplamı eşittir 0. Ara düğümler oluşturulurken ilk koşul korunur, bu nedenle sonunda sadece ikinci koşulun kontrol edilmesi gerekir.

Bant 5 düğümlü hücrelere bölünür,

i d x 0 0

burada i, dizin (soldan sağa doğru azalan) bir dkısmi toplamıdır ve xelementtir.

Kontrol akışının taslağı:

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)

Bazen bir değerin gerçek (kavramsal) değerden bir veya iki daha büyük olarak saklandığını veya başlatıldığını ve gerektiğinde ayarlandığını unutmayın.


3

Piton 3 ( 138 134 128 121 119 bayt)

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)]

Örnek çıktı 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 bir ikili düğümü, 0 bir tekli düğümü ve -1 bir terminal düğümünü temsil eder. Her ağacın sonunda zımni bir terminal düğümü vardır.

Program etrafta çok uzun sürüyor n=17.


3

JavaScript (Firefox 30-57), 79 bayt

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]]:[[]]

Burada -1bir terminali, 0bir tekli düğümü ve 1bir ikili düğümü temsil eder . m=14Bilgisayarımda yavaşlamaya başlar . Yinelenen ağacın sonundan geri çalışır.

  • Hesaplanmayan düğümlerin sayısı l, sonunda yalnızca 1 düğüm kalması gerçeğiyle sınırlıdır.
  • Bir sonraki düğümün tipi n, çocuk olması için yeterince hesaplanmamış düğüm bulunması gereksinimi ile sınırlıdır.

2

Prolog, 149 144 138 137 131 107 bayt

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

Ve çözümleri saymak

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

Python, 71 bayt

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

Bu, ağaçları virgül, boşluk ve en dışını kaldırarak ((((),), ()),)dönüştürülebilen iç içe tuples olarak temsil eder .((())())()

Daha önceki 76 baytlık bir sürüm:

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 bayt

Peter Taylor'ın CJam'ın cevapladığı farklı bir yaklaşım kullanır.

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

Çıktı gibi bir şey olacak 1110120020102100. Her ağaç bir nbasamak grubudur ( ngiriş numarası nerede ).

Temel fikir, biz her basamak olası dize oluşturmak olduğunu 0, 1ve 2, ve sonra iyi biçimli ağaçlar sadece olanları süzmek.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.