バイナリツリーを印刷する


18

SOに関する最近の質問に触発され...

次の形式でバイナリツリーを印刷する関数を作成します。

   3
 /   \
1     5
 \   / \
  2 4   6
  1. 出力は、ノードの行、それに続く関係の行/\文字、ノードの行などで構成されます。
  2. すべてのノードが単一の文字として表現可能であると想定できます。
  3. 最下位レベルの隣接ノードは、少なくとも1つのスペースで区切る必要があり、さらに上位のノードは必要に応じて区切る必要があります。
  4. 2つの子を持つノードは、直接の子の真ん中に正確に配置する必要があります。
  5. 関係のスラッシュは、親と適切な子の中間にある必要があります(どの方法でも)。

入力:

入力は関数の引数として提供されます。ツリーの正確な構造は指定しませんが、実際のバイナリツリーとして使用できる必要があります。「予想される出力のように見える文字列として、私のプログラムでツリーが表されていません」。

出力ストリームに出力するか、選択した出力を含む文字列を返すことができます。

最短のコードのポイントですが、90%の作業の短いソリューションよりも完全に機能する長いソリューションの方がずっと好きです。


報奨金の更新:

賞金のために、私(オプティマイザー)はわずかな変更を行っています:

  • 入力は、STDIN、ARGV、または関数引数からのものです。
  • 出力はSTDOUT(またはconsole.logJS)にある必要があります
  • たとえば、入力は配列形式であると想定できます。[1,2,3]または[1 2 3]

更新2-バイナリツリーは実際にはバイナリ検索ツリーである必要があります。最初にこれについて言及しなかったため、ユーザーが通常の配列をバイナリ検索ツリー配列に変換することを別個のプログラムとして扱うことができ、最終的なバイトカウントは、プログラムが配列を引数として取り込んで印刷するためのものです二分木のような。


適切な複数の関係スラッシュを使用する必要がありますか?最小数のスラッシュを使用する必要がありますか?単一の左の子と単一の右の子を持つことを区別する必要がありますか?すべての出力行に先頭のスペースを入れても大丈夫でしょうか?

ツリーが完成していない場合(nに対して2 ^ n-1ノード)、どうしますか?一部のノード(どれですか?)には子が1つしかありません。しかし、ノードが1つだけの子を持つノードを許可されている場合、縮退ツリーは簡単に作成できます(1-2-3-4-5-6の右下など)。
キースランドール14

多数の場合、どのように描画しますか?例30000,1000,499999
モーセン14

回答:


9

Fortran 77-1085文字

      subroutine q(u,t)
      implicit integer(i-z)
      character*33 f,g
      dimension t(u)
      m=ceiling(log(real(u))/log(2.))
      v=2**(m+1)-1
      do l=1,m
         n=2**(l-1)
         k=2**(m-l+2)-3
         w=(3+k)*2**(l-1)-k
         p=1+(v-w)/2
         if(l.ne.1)then
            write(f,'(A,I3,A)')'(A',p,',$)'
            print f,' '
            write(f,'(A5,I3,A3)')'(A3,A',k,',$)'
            do j=2**(l-1),2**l-1
               if(t(j/2).lt.0.or.t(j).lt.0)then
                  print f,'   ',' '
               elseif(mod(j,2).eq.0)then
                  print f,'  /',' '
               else
                  print f,' \ ',' '
               endif
            enddo
            print*
         endif
         write(f,'(A,I3,A)')'(A',p,',$)'
         print f,' '
         write(f,'(A5,I3,A3)')'(I3,A',k,',$)'
         write(g,'(A2,I3,A3)')'(A',k+3,',$)'
         do j=2**(l-1),2**l-1
            if(t(j).ge.0)then
               print f,t(j),' '
            else 
               print g,' '
            endif
         enddo
         print*
      enddo
      end

ツリーは、入力配列tで通常の​​方法で表されます。ルート1、ルート2、左2、ルート3、右3、ルート4、左4で...

出力は、最大5レベルの深さの従来の端末に収まる必要があります。

ノードの各ペアの間にスラッシュを1つだけ使用します。4つ以上のレベルがあると、上部付近でかなりおかしく見えます。最大3桁のノードを許可しました。

コメントと起動スキャフォールドを含む完全なプログラム:

      program tree

      parameter (l=8)          ! How many nodes to support
      dimension i(l)

c     Initialize the array to all empty nodes
      do j=1,l
         i(j)=-1
      end do
c     Fill in some values
      i(1)=3
      i(2)=1
      i(3)=5
      i(5)=2
      i(6)=4
      i(7)=7
c      i(14)=6
c      i(15)=8
c     Call the printing routine
      call q(l,i)

      stop
      end

c     Print an ASCII representation of the tree
c
c     u the length of the array containing the tree
c     t an integer array representing the tree.
c
c     The array contains only non-negative values, and empty nodes are
c     represented in the array with -1.
c
c     The printed representation uses three characters for every node,
c     and places the (back) slash equally between the two node-centers.
      subroutine q(u,t)
      implicit integer(i-z)
      character*33 f,g
      dimension t(u)
      m=ceiling(log(real(u))/log(2.)) ! maximum depth of the tree
      v=2**(m+1)-1              ! width needed for printing the whole tree
                                ! Optimized from 3*2**m + 1*((2**m)-1) at
                                ! the bottom level
      do l=1,m
         n=2**(l-1)             ! number of nodes on this level
         k=2**(m-l+2)-3         ! internode spacing
         w=(3+k)*2**(l-1)-k     ! width needed for printing this row
                                ! Optimized from 3*2**l + k*((2**l)-1) at
                                ! the bottom level
         p=1+(v-w)/2            ! padding for this row
c     Print the connecting lines associated with the previous level
         if(l.ne.1)then         ! Write the connecting lines
            write(f,'(A,I3,A)')'(A',p,',$)'
            print f,' '
            write(f,'(A5,I3,A3)')'(A3,A',k,',$)'
            do j=2**(l-1),2**l-1
               if(t(j/2).lt.0.or.t(j).lt.0)then
                  print f,'   ',' '
               elseif(mod(j,2).eq.0)then
                  print f,'  /',' '
               else
                  print f,' \ ',' '
               endif
            enddo
            print*
         endif
c     Print the nodes on this level
         write(f,'(A,I3,A)')'(A',p,',$)'
         print f,' '
         write(f,'(A5,I3,A3)')'(I3,A',k,',$)'
         write(g,'(A2,I3,A3)')'(A',k+3,',$)'
         do j=2**(l-1),2**l-1
            if(t(j).ge.0)then
               print f,t(j),' '
            else 
               print g,' '
            endif
         enddo
         print*
      enddo
      end

例と同等の入力での出力:

$ ./a.out 
         3             
     /      \      
     1       5     
      \    /  \  
       2   4   7 

なぜこの言語なのか?
tomsmeding

9
なぜならそれはそう悪いゴルフに適しています。
dmckee 14

5

CJam、100 99バイト

q~_,2b,)2\#:Q1@{_2$<Q(S*:T*TQ2/:Q@ts[N+_0@{@1$' >{2$St2$_Q3*&2/^_4$>"\/"=t}*@)}/;U*o]o1:U$>\2*\}h];

入力は、ASCII制御文字を含まない文字のリストでなければなりません。空のノードはスペースで示されます。また、正確に2 n -1ノードの完全な二分木でなければなりません。

例:

['6 '3 '7 '1 '4 '  '9 '0 '2 '  '5 '  '  '8 ' ]

または、単に文字列を使用します。

"63714 902 5  8 "

出力:

                6              
            /       \          
        3               7      
      /   \               \    
    1       4               9  
   / \       \             /   
  0   2       5           8    

説明

q~                        " Read input. ";
_,2b,                     " Get tree height. ";
)2\#:Q                    " Get (displayed) tree width and save it in Q. ";
1@                        " Push X=1 and rotate the input to top. ";
{                         " Do: ";
    _2$<                  " Get first X characters from the input. ";
    Q(S*:T                " T = (Q-1) spaces. ";
    *                     " Separate the X characters by T. ";
    TQ2/:Q@t              " Put the string in the middle of T, and divide Q by 2. ";
    s                     " Concatenate everything into a string.
                            This is the line of node labels. ";
    [
        N+                " Append a newline. ";
        _                 " Duplicate. ";
        0@                " Push a 0 and rotate the original string to top. ";
        {                 " For each character: ";
            @             " Rotate the duplicate to top. ";
            1$' >         " Test if the current character is greater than a space. ";
            {             " If true: ";
                2$St      " Set the current character in the duplicate to space. ";
                2$        " Copy the current position I (the number initialized with 0). ";
                _Q3*&2/^  " Calculate I ^ ((I & (3*Q))>>1),
                            the position of the relationship character. ";
                _4$>      " Test if it is greater than the current position. ";
                "\/"=     " Select the relationship character. ";
                t         " Change the character in the duplicate. ";
            }*
            @)            " Increment the current position. ";
        }/
                          " The last two items are the line of relationship characters
                            and the tree width. ";
        ;                 " Discard the tree width. ";
        U*                " If it is the first line, empty the line of
                            relationship characters. ";
        o                 " Output. ";
    ]o                    " Output the line of node labels. ";
    1:U                   " Mark it not the first line. ";
    $>                    " Remove the first X characters from the input. ";
    \2*\                  " Multiply X by 2. ";
}h                        " ...while the input is not empty. ";
];                        " Discard everything in the stack. ";

変換スクリプト

[[0LL]W]
[q~{_a_:i={'0+}*}%La2*f+
_,,]z$
1$a+
{
    {
        1$1=1$1=>:T
        {
            ~@0=2 3$1=t
            @1@ta\+
        }*
        T
    }g
}*
0=1=a
{
    {
        (M\+:M;
        La[' LL]aer~
    }%
    _[' LL]a-
}g
];
M0+`-3<']+

文字または1桁の数字を受け入れます。

例(すべて同じ):

['6 '7 '9 '3 '1 '2 '8 '4 '0 '5]
[6 7 9 3 1 2 8 4 0 5]
"6793128405"

出力:

['6 '3 '7 '1 '4 ' '9 '0 '2 ' '5 ' ' '8 ' ]

これは単純なデカルトツリー構造です。


さらに2バイトを追加して、変換スクリプトの入力を文字ではなく適切な整数にすることができます:)
オプティマイザー14

@Optimizerが両方をサポートするように編集されました。文字は単一の文字を持つノード名のみをサポートするため、文字の方が意味があると思います。1桁の数字よりもはるかに多くの文字があります。
jimmy23013 14

2

Python 2、411バイト

import math
def g(a,o,d,l,b):
 if l<0:
    return
 c=2*b+1
 k=2*l+1
 o[k]=' '*b
 n=d-l
 p=1 if n==0 else 3*2**n-1
 o[k-1]=p/2*' '
 i=0
 for v in a[2**l-1:2**l*2-1]:
    v=' ' if v==None else v
    o[k]+=v+' '*c
    m=' ' if v==' ' else '/' if i%2==0 else '\\'
    o[k-1]+=m+max(1,b)*' ' if i%2==0 else m+p*' '
    i+=1

 g(a,o,d,l-1,c)
def f(a):
 d=int(math.log(len(a),2))
 o=['']*(d*2+2)
 g(a,o,d,d,0)
 print '\n'.join(o[1:])

注:最初のインデントレベルは1スペース、2番目は1タブです。

たとえばf、1文字の文字列またはNone'のリストを使用して呼び出します。f(['1',None,'3'])。リストを空にすることはできません。

これは、賞金の規則に従う必要があります。

コンバータースクリプト:

バイナリツリープリンターで使用される形式に変換して配列します。例:

$ python conv.py [3,5,4,6,1,2]
['3', '1', '5', None, '2', '4', '6']

-

import sys

def insert(bt, i):
    if i < bt[0]:
        j = 0
    else:
        j = 1

    n = bt[1][j]
    if n == [None]:
        bt[1][j] = [i, [[None], [None]]]
    else:
        insert(bt[1][j], i)

def insert_empty(bt, i):
    if i == 0:
        return
    if bt == [None]:
        bt += [[[None], [None]]]

    insert_empty(bt[1][0], i - 1)
    insert_empty(bt[1][1], i - 1)

def get(l, level):
    if level == 0:
        if type(l) == list:
            return ([], l)
        else:
            return ([l], [])
    elif type(l) != list:
        return ([], [])

    res = []
    left = []

    for r, l in  [get(i, level - 1) for i in l]:
        res += r
        left += l

    return (res, left)

if __name__ == '__main__':
    l = eval(sys.argv[1])
    bt = [l[0], [[None], [None]]]
    map(lambda x: insert(bt, x), l[1:])

    depth = lambda l: 0 if type(l) != list else max(map(depth, l)) + 1
    d = (depth(bt) + 1) / 2

    insert_empty(bt, d - 1)

    flat = []
    left = bt
    i = 0
    while len(left) > 0:
        f, left = get(left, 1)
        flat += f

        i += 1

    for i in range(len(flat) - 1, -1, -1):
        if flat[i] == None:
            flat.pop()
        else:
            break

    flat = map(lambda x: None if x == None else str(x), flat)

    print flat

例:

これらを実行するには、メインファイルの名前bt.pyとコンバータファイルの名前が必要conv.pyです。

$ python conv.py [3,5,4,6,1,2] | python -c 'import bt; bt.f(input())'
   3
  / \
 1   5
  \ / \
  2 4 6
$ python conv.py [5,4,3,7,9] | python -c 'import bt; bt.f(input())'
   5
  / \
 4   7
/     \
3     9
$ python conv.py [1,2,3,4,5,6] | python -c 'import bt; bt.f(input())'
                               1
                                       \
                                               2
                                                   \
                                                       3
                                                         \
                                                           4
                                                            \
                                                             5
                                                              \
                                                              6
$ python conv.py [6,5,4,3,2,1] | python -c 'import bt; bt.f(input())'
                                   6
                       /
               5
           /
       4
     /
   3
  /
 2
/
1

実際にバイナリツリーを作成しているわけではありません。配列をバイナリツリーとして印刷するだけです。['1','2','3','4','5','6','7','8','9']配列の出力は、示したものではありません。ルート要素32ある正しい子として、正しい子として持つ必要があり1ます。
オプティマイザー14

@Optimizer質問:「入力は関数の引数として提供されます。ツリーの正確な構造は指定しませんが、実際のバイナリツリーとして使用可能でなければなりません。」使用される配列形式の特定の定義が表示されません。
ティロ14

質問は元々バイナリツリーの印刷に関するものです。出力はバイナリツリーではありません。配列のフォーマットは、それとは何の関係もありません。
オプティマイザー14

@Optimizerはどうして二分木ではないのですか?ウィキペディアから:バイナリツリーは、各ノードが最大で2つの子を持つツリーデータ構造です。ノードのいずれかに3つ以上の子がありますか?
ティロ14

うん なるほど。ここに誤解という用語があると思います。最初の例でも、出力はバイナリ検索ツリーの形式です。そして、私の賞金はまた、バイナリ検索ツリーのみです。混乱して申し訳ありません。
オプティマイザー14

1

APL、125文字

{⍵{x←⍵[0;d←⌈2÷⍨1⌷⍴⍵]←↑⍺
2 1∇{x[2↓⍳↑⍴x;(⍳d)+d×⍺-1]←⍵(⍺⍺⍣t←0≠⍴⍵)2↓x[;⍳d]
x[1;d+(⌈d÷4)ׯ1*⍺]←' /\'[t×⍺]}¨⍺[2 1]
x}' '⍴⍨2(×,*)≡⍵}

例:

{⍵{x←⍵[0;d←⌈2÷⍨1⌷⍴⍵]←↑⍺
2 1∇{x[2↓⍳↑⍴x;(⍳d)+d×⍺-1]←⍵(⍺⍺⍣t←0≠⍴⍵)2↓x[;⍳d]
x[1;d+(⌈d÷4)ׯ1*⍺]←' /\'[t×⍺]}¨⍺[2 1]
x}' '⍴⍨2(×,*)≡⍵}('1' ('2' ('3' ('4' ()()) ('5' ()())) ('6' ()('7' ()())))('8' ()('9' ('0' ()())())))

ここでテストしました。


これも変換スクリプトですか?
オプティマイザー14

@Optimizerネストされた配列入力形式を使用します。これはおそらくバイナリ検索ツリーとして使用できます(ただし、複雑さについてはわかりません)。もっと一般的なフォーマットを使用する必要がある場合は...後で行うかもしれません。
jimmy23013 14

@Optimizer質問をもう一度読んで、「バイナリ検索ツリー配列」とは、完全なバイナリツリーの深さの配列(または他の何か)を意味しますか?特定のことだとは思いませんでした。そして、この用語を検索しても、何も有用ではありませんでした。
jimmy23013 14


@Optimizerまあ、それはまさに私が意味したものでした。しかし、私はそれが通常「バイナリ検索ツリー配列」と呼ばれるのではなく、「バイナリツリーの一種の配列ストレージ」としか考えていません。...それはおそらく、いくつかの明確化が必要...そして私はおそらく日後にこの答えを修正します、多分別の言語で
jimmy23013

0

ルビー、265バイト

def p(t);h=Math.log(t.length,2).to_i;i=-1;j=[];0.upto(h){|d|s=2**(h-d)-1;c=2**d;if d>0;m=' '*(s+s/2)+'I'+' '*(s-s/2);1.upto(d){m+=' '+m.reverse};w=i;puts m.gsub(/I/){|o|t[w+=1]?(w%2==0?'\\':'/'):' '};end;puts (0...c).map{' '*s+(t[i += 1]or' ').to_s+' '*s}*' ';};end

@proudhaskellerバージョン、269バイト

def p(t);h=Math.log(t.length,2).to_i;i=-1;j=[];0.upto(h){|d|s=(z=2**(h-d))-1;c=2**d;if d>0;m=' '*(s+z/2)+'I'+' '*(s-z/2);1.upto(d){m+=' '+m.reverse};w=i;puts m.gsub(/I/){|o|t[w+=1]?(w%2==0?'\\':'/'):' '};end;puts (0...c).map{' '*s+(t[i += 1]or' ').to_s+' '*s}*' ';};end

説明

詳細バージョン:

def p(t)
  depth = Math.log(t.length, 2).floor
  i = -1
  j = []
  (0..depth).each do |d|
    s = 2 ** (depth-d)-1
    c = 2 ** d

    if d > 0
      m = ' '*(s+s/2) + '|' + ' '*(s-s/2)
      w = i
      1.upto(d) { m += ' ' + m.reverse }
      puts m.gsub(/\|/) { |o| t[w+=1] ? (w%2==0 ? '\\' : '/') : ' ' }
    end

    puts (0...c).map{' '*s+(t[i += 1]or' ').to_s+' '*s}*' '
  end
end

n = nil
p([
  1, 2, 3, 4, 5,
  n, 7, 8, 9, 0,
  1, n, n, 4, 5,
  6, 7, 8, 9, 0,
  1, 2, 3, n, n,
  n, n, 8, 9, n,
  n
])

与える:

               1               
          /         \          
       2               3       
    /     \               \    
   4       5               7   
 /   \   /   \           /   \ 
 8   9   0   1           4   5 
/ \ / \ / \ / \         / \    
6 7 8 9 0 1 2 3         8 9   

(変換スクリプトはまだ書いていません。)


あなたのスラッシュがちょうど真ん中にない
誇りに思っているhaskeller 14

@proudhaskeller「好きな方法でラウンド」、私はそれがこのようにクールに見えると思った。必要に応じて、s / 2を(s + 1)/ 2に置き換えることができます。
AlexRath 14

いいえ、最初の行のスラッシュは真ん中にありません。この行では、これは丸めの問題ではありません
誇り高いhaskeller 14

@proudhaskeller s / 2を(s + 1)/ 2に置き換えると、それらはちょうど真ん中にありますが、左端と右端の枝が丸く見えるので、この方法の方が好きです。
AlexRath 14

それは...仕様に反している
誇りhaskeller
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.