이진 트리 열거


20

이진 나무

이진 트리는 세 가지 유형의 노드가있는 트리입니다.

  • 자식이없는 터미널 노드
  • 하나의 자식이있는 단항 노드
  • 각각 2 개의 자식이있는 이진 노드

BNF (Backus–Naur 형식)로 주어진 다음 문법으로 표현할 수 있습니다 .

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

<terminal> ::= 
    "0"

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

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

이 문법에서 노드는 사전 순서대로 제공되며 각 노드는 자식 수인 숫자로 표시됩니다.

모츠 킨 숫자

Motzkin 숫자 ( OEIS ) ( Wikipedia )는 많은 해석을 가지고 있지만 한 가지 해석은 nMotzkin 숫자는 n노드 가있는 별개의 이진 트리의 수입니다 . Motzkin 숫자 표가 시작됩니다

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 개의 노드가있는 9 개의 이진 트리는

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)  

중복 트리를 확인할 수있는 장소

중복을 확인하는 곳은 M (5)입니다.
이 하나의 나무는 M (4) 나무에서 M (5)에 대해 두 번 생성되었습니다.

(2 (1 0) (1 0))  

첫 번째에 단항 분기를 추가하여

(2 (1 0) 0)

둘째로 단항 분기를 추가하여

(2 0 (1 0))

BNF 이해

BNF는 간단한 규칙으로 구성됩니다.

<symbol> ::= expression

여기서 왼쪽은로 둘러싸인 기호 이름 <>입니다.
오른쪽에는 심볼을 구성하는 표현이 있습니다. 일부 규칙은 구성에서 다른 규칙을 사용합니다 (예 :

<e> ::= <terminal>

e 될 수 있습니다 terminal

일부 규칙에는 기호를 구성하는 데 사용되는 문자가 있습니다. 예 :

<terminal> ::= "0"

terminal 문자 0입니다.

일부 규칙에는 여러 가지 방법으로 구성 할 수 있습니다. 예 :

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

ea <terminal>또는 a <unary>또는 a 일 수 있습니다 <binary>.

그리고 일부 규칙은 일련의 부분입니다.

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

A는 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"가 무엇인지 명확히하라
Luis Mendo

1
@GuyCoder 내 요점은, 누군가 "BNF"를보고 그것이 무엇을 의미하는지 모르는 경우, 학생들이 책을 벗고 읽기를 중단 할 수 있다는 것을 모른다. 아마도 약어 대신 이름을 사용하고 Wikipedia에 링크를 추가하면 충분합니다.
Luis Mendo

4
@ mbomb007 이름이 변경되었습니다. 이에 대한 동료 압력 상을 받아야합니다. :)
Guy Coder

답변:


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

CJam (37 바이트)

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

온라인 데모 . 이것은 매우 효율적이지 않으므로 5온라인에서 입력 을 계산하지 않으려는 경우가 있습니다.

따라야 할 해부.


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.

관심의 대상 : Pyth 소스 코드
Guy Coder

4

brainfuck, 107 바이트

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

형식화 :

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

온라인으로 사용해보십시오

입력은 바이트 로 취해지고 트리는 다음과 같이 12100표현됩니다 \x01\x02\x03\x02. tr/\x01\x02\x03/012/문자열을 역변환 , 변환 , 역전하고 final을 추가합니다 0. 나무는로 구분됩니다 \xfe. (예를 들어, 출력은 제을 변경하여 읽기 쉽게 할 수 --36상기 .+47.-47, -3636 개의 캐릭터 수단 -문자 등)

이 방법은 (벤 프랭클도 사용했던) 속성을 사용합니다 : 가능한 노드를 고려하고 -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)

때로는 값이 실제 (개념적) 값보다 1-2 배 큰 값으로 저장 또는 초기화되어 필요에 따라 조정되는 경우가 있습니다.


3

파이썬 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

자바 스크립트 (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=14PC 에서 속도가 느려지기 시작합니다 . 트리 끝에서 재귀 적으로 작동합니다.

  • 설명되지 않은 노드의 l수는 끝에 하나의 노드 만 남게되므로 제한됩니다.
  • 다음 노드의 유형은 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 바이트

Peter Taylor의 CJam이 대답하는 다른 접근법을 사용합니다.

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

출력은 다음과 같습니다 1110120020102100. 각 트리는 n숫자 그룹입니다 ( n입력 번호는 어디에 있습니까 ).

기본적인 아이디어는 우리가 숫자의 각각의 가능한 문자열을 생성한다는 것입니다 0, 1그리고 2다음 잘 형성 나무 만 사람을 필터링 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.