打印二叉树


18

受最近关于SO的问题启发...

编写函数以以下格式打印二叉树:

   3
 /   \
1     5
 \   / \
  2 4   6
  1. 输出应由一行节点组成,然后由一行/\表示关系的字符组成,再由一行节点等。
  2. 您可以假设所有节点都可以表示为单个字符。
  3. 最低层的相邻节点应至少隔开一个空间,上方的节点应适当分开。
  4. 有两个孩子的节点应精确地放置在其直接孩子的中间。
  5. 亲子关系应该在父母和合适的孩子之间的中间位置(无论您选择哪种方式)。

输入:

输入将作为函数的参数提供。我不会指定树的确切结构,但是它必须可用作实际的二叉树。在我的程序中,没有“树以字符串的形式同时显示为预期的输出”。

您可以打印到输出流或返回包含您选择的输出的字符串。

最短代码的要点,但是与90%正常工作的短解决方案相比,我更喜欢完整工作的长期解决方案。


更新赏金:

为了奖励,我(优化程序)进行了一些小的更改:

  • 输入可以来自STDIN,ARGV或函数参数。
  • 输出必须在STD​​OUT上(或console.log对于JS)
  • 您可以假设输入为数组形式,例如。[1,2,3]要么[1 2 3]

更新2-二进制树实际上应该是二进制搜索树。由于我最初没有提到这一点,因此我将允许用户将将普通数组转换为二进制搜索树数组作为单独的程序对待,而最终字节数仅用于程序将数组作为参数并打印出来像二叉树。


我们应该使用几个适当的关系斜线吗?我们必须使用最小数量的斜线吗?是否应该区分生一个独生子女和一个独生子女?每条输出线中都应有空格吗?

如果树不完整(某些n为2 ^ n-1个节点),我们该怎么办?有些节点(哪些?)只有一个孩子。但是如果允许我们的节点只有一个孩子,那么退化的树就很容易制作(比如说,在右下方的1-2-3-4-5-6)。
基思·兰德尔

如何绘制大量数字?例如30000,1000,499999
Mohsen

Answers:


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处,root->左在2处,根->右在3处根->左->左在4处...

输出应适合多达5级深度的常规端子。

我在每对节点之间使用一个斜杠,一旦有四个或更多的级别,在顶部附近看起来就很傻了。我最多允许三个数字节点。

带有注释和启动支架的完整程序:

      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 2014年

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<']+

它接受字符或一位数字。

例子(都是一样的):

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

这是直截了当的笛卡尔树结构。


您可以再添加两个字节,并使转换脚本的输入为正确的整数而不是字符:)
Optimizer

@Optimizer编辑为两者都支持。我认为字符更有意义,因为它仅支持单个字符的节点名称。字符多于一位数字。
jimmy23013

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个空格,第二个是一个制表符。

f用一个单字符字符串或的列表进行调用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']数组的输出不是您显示的。3作为一个正确的孩子,它应该是一个正确的孩子,2而这个孩子1是一个根元素。
Optimizer

@Optimizer从问题开始:“输入将作为函数的参数提供。我不会指定树的确切结构,但是它必须可用作实际的二叉树。” 我没有看到所使用的数组格式的特定定义。
Tyilo 2014年

本来的问题是关于打印二叉树。您的输出不是二叉树。数组的格式与它无关。
Optimizer

@Optimizer它们不是二叉树吗?摘自Wikipedia:二叉树是一种树数据结构,其中每个节点最多具有两个子节点。节点中是否有两个以上的子节点?
Tyilo 2014年

嗯 我现在明白了。我认为这里有一个误解。即使在最初的示例中,输出也是二进制搜索树的格式。而且我的赏识还仅限于二叉搜索树。很抱歉给您带来混乱。
Optimizer

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' ()())())))

在这里测试。


这也是转换脚本吗?
Optimizer

@Optimizer它采用嵌套数组输入格式,可以将其用作二进制搜索树(但我不确定其复杂性)。如果我必须使用一些更常用的格式,也许以后再做。
jimmy23013 2014年

@Optimizer现在再次阅读问题,“二叉搜索树数组”是否表示按深度顺序(或其他方式)的完整二叉树的数组?我认为没有什么特别的。搜索这个词并没有提供任何有用的信息。
jimmy23013


@Optimizer好吧,这就是我的意思。但是我不认为它通常被称为“二进制搜索树数组”,而只是“一种存储二进制树的数组”。它可能需要澄清一下……几天后,我可能会用另一种语言来解决此问题……
jimmy23013 2014年

0

Ruby,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 2014年

@proudhaskeller“无论您要哪种方式”,我都认为这种方式看起来更酷。如果需要,可以将(s / 2)替换为(s + 1)/ 2。
AlexRath 2014年

不,第一行中的斜线不完全位于中间,在这一行中,这不是四舍五入的问题
骄傲的haskeller 2014年

@proudhaskeller如果将s / 2替换为(s + 1)/ 2,它们恰好在中间,但是我仍然更喜欢这种方式,因为它使最左边和最右边的分支看起来很圆。
AlexRath 2014年

这违反规范...
骄傲的haskeller 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.