ระบุต้นไม้ไบนารี


20

ต้นไม้ไบนารี

ต้นไม้ไบนารีเป็นต้นไม้ที่มีโหนดสามประเภท:

  • โหนดเทอร์มินัลซึ่งไม่มีลูก
  • โหนดเดียวซึ่งมีลูกหนึ่งคน
  • โหนดฐานสองซึ่งมีลูกสองคน

เราสามารถเป็นตัวแทนของพวกเขาด้วยไวยากรณ์ต่อไปนี้ที่กำหนดในBNF (รูปแบบ Backus – Naur):

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

<terminal> ::= 
    "0"

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

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

ในไวยากรณ์นี้โหนดจะได้รับในการสั่งซื้อล่วงหน้าและแต่ละโหนดจะมีตัวเลขซึ่งเป็นจำนวนของเด็กที่มี

ตัวเลข Motzkin

หมายเลข Motzkin ( OEIS ) ( Wikipedia ) มีการตีความหลายอย่าง แต่หนึ่งการตีความคือnหมายเลข Motzkin ที่สามคือจำนวนของต้นไม้ไบนารีที่แตกต่างกับ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 โหนดคือ

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 (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สามารถเป็น<terminal>หรือหรือ<unary><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 คน ฉันคิดว่า unary-binary เป็นเพียงคำที่เฉพาะเจาะจงมากขึ้นซึ่งไม่ได้หมายถึงอะไรที่แตกต่าง
fəˈnɛtɪk

พิจารณาให้ชัดเจนว่า "BNF" สำหรับพวกเราที่ไม่ได้เป็นนักวิทยาศาสตร์คอมพิวเตอร์
Luis Mendo

1
@GuyCoder จุดของฉันคือถ้ามีคนเห็น "BNF" และไม่ทราบว่านั่นหมายความว่าพวกเขาอาจถูกเลื่อนออกไปและหยุดอ่าน บางทีการใช้ชื่อแทนคำย่อและเพิ่มลิงก์ไปยัง Wikipedia ก็เพียงพอแล้ว
Luis Mendo

4
@ mbomb007 เปลี่ยนชื่อแล้ว ฉันควรได้รับรางวัลจากแรงกดดันจากเพื่อน :)
Guy Coder

คำตอบ:


12

Haskell, 68 ไบต์

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

เทอร์มินัลโหนดถูกแสดงโดย0, unary และโหนดไบนารีโดย(e)resp (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 ไบต์)

นี้อยู่บนพื้นฐานของฉันแก้ปัญหาหลาม 3

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

เป็นครั้งแรกที่ฉันใช้ Pyth ดังนั้นนี่จึงเป็นโอกาสที่จะเล่นกอล์ฟได้

ตัวอย่างเอาต์พุตเมื่ออินพุตคือ4:

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

1 หมายถึงโหนดไบนารี 0 หมายถึงโหนด unary และ -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/ ต้นไม้จะถูกคั่นด้วย0 \xfe(เอาต์พุตสามารถอ่านได้ง่ายขึ้นเช่นการเปลี่ยนครั้งแรก-ใน-36และเปลี่ยน.เข้า+47.-47ซึ่ง-36หมายถึงสตริง 36 -ตัวอักษร ฯลฯ )

วิธีการนี้ใช้ประโยชน์จากคุณสมบัติ (ซึ่ง Ben Frankel ใช้ด้วย): เมื่อพิจารณาถึงโหนดที่เป็นไปได้-1, 0, 1และไม่สนใจ-1โหนดสุดท้ายรายการจะแสดงแผนภูมิต้นไม้ที่ถูกต้องหากว่า (1) คำนำหน้าทั้งหมดของรายการมีผลรวมที่ไม่เป็นลบ และ (2) 0ผลรวมของรายการทั้งหมดเท่ากับ เงื่อนไขแรกถูกรักษาไว้ในขณะที่สร้างโหนดระดับกลางดังนั้นจึงต้องตรวจสอบเงื่อนไขที่สองในตอนท้ายเท่านั้น

เทปแบ่งออกเป็นเซลล์ 5 โหนด

i d x 0 0

โดยที่iindex (จากซ้ายไปขวา) 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 หมายถึงโหนด unary และ -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โหนด unary และ1โหนดไบนารี เริ่มช้าลงm=14บนพีซีของฉัน กลับมาใช้ซ้ำจากปลายต้นไม้

  • จำนวนโหนดที่ไม่ได้lถูกเมาท์ถูก จำกัด โดยความจริงที่ว่าอาจมีเพียง 1 โหนดที่เหลืออยู่ในตอนท้าย
  • ชนิดของโหนดถัดไปnนั้นถูก จำกัด โดยความจำเป็นที่จะต้องมีโหนดที่ไม่มีผู้ใช้เพียงพอที่จะเป็นโหนดลูก

2

Prolog, 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

Python ขนาด 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 ของ Peter Taylor

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

1110120020102100ส่งออกจะเป็นสิ่งที่ชอบ ต้นไม้แต่ละต้นเป็นกลุ่มของnตัวเลข (โดยที่nคือหมายเลขอินพุต)

ความคิดพื้นฐานคือการที่เราสร้างแต่ละสายเป็นไปได้ของตัวเลข0, 1และ2แล้วกรองเฉพาะคนที่มีต้นไม้รูปแบบที่ดี

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.