从数组中绘制树


24

给定一个可能嵌套的非空单位数字正整数(不能保证唯一)的数组,请使用箱形绘图字符将ASCII艺术形式的输出输出为树┌ ┴ ┐ ─ │ ┬ ┼。(这些是从“代码页437”复制的,但是您可以使用任何等效的表示形式)。

数组的每个整数都应该是树的叶子。数组中处于同一级别的元素应位于树的同一级别。所有元素都应由足够的空格分隔以区分(由您决定宽度之间的最小间隔)。

例如,给定array [[1, [2]], [3, [4, 5]]],输出以下树

 ┌─┴─┐
┌┴┐ ┌┴─┐
1 │ 3 ┌┴┐
  2   4 5

对于数组[1, 2, 3],树可能看起来像

┌─┼─┐
1 2 3

但是数组[[1, 2, 3]]看起来像

  │
┌─┼─┐
1 2 3

虽然数组[1, [1, [1, [1]]]]看起来像

 ┌─┴┐
 1 ┌┴─┐
   1 ┌┴┐
     1 │
       1

作为更复杂的示例,[1, [[[2, 3], 4], 5]]可能是

┌┴───┐
1  ┌─┴┐
 ┌─┴┐ 5
┌┴┐ 4
2 3

或其他几种变体。


  • 输入和输出可以通过任何方便的方法给出。
  • 您可以将其打印到STDOUT或将其作为功能结果返回。
  • 完整的程序或功能都是可以接受的。
  • 只要字符正确排列,任何数量的外部空格都是可以接受的。
  • 禁止出现标准漏洞
  • 这是因此所有常见的高​​尔夫规则都适用,并且最短的代码(以字节为单位)获胜。

[1,[[[2,3],4],5]]可能是一个有趣的测试用例,因为它需要人为地扩展根,以便右子树不会与左子树发生冲突。

@Poke添加作为示例。该测试用例可能有几种变体。
AdmBorkBork

2
该测试用例的第一个示例不正确。这表明,在S第二元件旁边的1是3项的数组:[2,3]4,和5。但是4和5不相邻。
Draco18s

4
看起来像[1, [[[2, 3]], [4], 5]]我。
尼尔

这些替代输入格式中的哪些(如果有)是可以接受的?
很有可能

Answers:


12

Python 3中400个 393 390字节

L=len
S,*K=' ┴┼│123456789'
def T(x):
 try:return[str(x+0)]
 except:
  z=[*map(T,x)];q=max(map(L,z))
  for p in z:p+=[S*L(p[0])]*(q-L(p))
  b=[S.join(a)for a in zip(*z)];t=b[0];l=L(t);s=0;e=L(z);r=[S]*l
  if e<2:return['│'.center(l),*b]
  for i in range(l):
   if t[i]in K:s+=1;r[i]='┬┌┐'[(s<e)-(s>1)]
   elif 0<s<e:r[i]='─'
  c=l//2;r[c]=K[r[c]=='┬'];return[''.join(r),*b]

从上到下返回字符串列表。

编辑1:通过避免重复┴┼(净节省2个字节),从一个字符串中切出0,更改从┬┌┐(使用<代替==)中选择绘图字符的方式以及用L(z)I 代替的方式修剪了7个字节e

编辑2:感谢ovs -2个字节,感谢凯文·克鲁伊森-1个字节

在线尝试!

不打高尔夫球

def layer(item):
    if isinstance(item, int):
        return [str(item)]
    else:
        subs = [layer(sub) for sub in item]
        longest = max(map(len, subs))
        for sub in subs:
            sub += [' ' * len(sub[0])] * (longest - len(sub))
        below = [' '.join(l) for l in zip(*subs)]
        top = below[0]
        l = len(top)
        if len(subs) == 1:
            return ['│'.center(l), *below]
        seen = 0
        expected = len(subs)
        builder = [' '] * l
        for i in range(l):
            c = top[i]
            if c in '┴┼│123456789':
                seen += 1
                if seen == 1:
                    builder[i] = '┌'
                elif seen == expected:
                    builder[i] = '┐'
                else:
                    builder[i] = '┬'
            elif 0 < seen < expected:
                builder[i] = '─'
        center = l // 2
        if builder[center] == '┬':
            builder[center] = '┼'
        else:
            builder[center] = '┴'
        return [''.join(builder), *below]

从叶子向上建造一棵树,一次一层。


2
我已将测试用例添加到您的TIO链接中尝试在线!
pizzapant184

好答案!通过将空间分配给这样的变量,您可以将其缩短两个字节S,*K=' ┴┼│123456789'
ovs

1
e==1可以是e<2保存一个字节(我不认为它永远不能为0,因为质询说明输入为非空-并且max(map(L,z))在这种情况下空输入无论如何都会失败。)
Kevin Cruijssen

3

干净544个 506字节

转义用于避免SE / TIO上的无效UTF-8,但由于它们是有效的文字,因此被计为一个字节

import StdEnv,Data.List;::T=I Int|L[T];$l#m= @l#k=map maxList(transpose m)=flatlines[[last[' ':[(\_|v<0|w<[j]|q>hd w|q<last w|any((==)q)w|q==j='\305'='\302'|q==j='\301'='\304'='\277'='\332'='\263'=toChar v+'0')0\\[v,r,j:w]<-m|r/2==p&&q>=hd w&&q<=last w]]\\q<-[0..k!!3]]\\p<-[0..k!!1]];@(L l)#p=twice(\p=[[v,r+1:[e+sum([2\\[v:_]<-i|0<=v]++zipWith(\c j=j!!2-c!!3)t(takeWhile(\[z:_]=v+z< -1)(tl t)))-x!!1\\e<-x]]\\i<-inits p&t<-tails p&[v,r:x]<-p])(concatMap@l)#g=[g\\[_,2,g:_]<-p]=[[-1,0,(hd g+last g)/2:g]:p];@(I i)=[[i,0,0,0]];

在线尝试!

以以下格式输入 L[I 3, L[I 4, I 5], I 2]..

从下至上,从左到右连接树,然后从右到左调整距离。

美化,排序:

import StdEnv, Data.List;
:: T = I Int | L [T];
$ l
    #m = @l
    #k = map maxList (transpose m)
    = flatlines [
        [
            last[
                ' ':
                [
                    if(v < 0)
                        if(w < [j])
                            if(q > hd w)
                                if(q < last w)
                                    if(any ((==) q) w)
                                        (
                                            if(q == j)
                                                '\305'
                                                '\302'
                                        )(
                                            if(q == j)
                                                '\301'
                                                '\304'
                                        )
                                    '\277'
                                '\332'
                            '\263'
                        (toChar v + '0')
                    \\ [v, r, j: w] <- m
                    | r/2 == p && q >= hd w && q <= last w
                ]
            ]
            \\ q <- [0..k!!3]
        ]
        \\p<-[0..k!!1]
    ];
@ (L l)
    #p = twice
        ( \p
            = [
                [
                    v, r + 1:
                    map
                        (
                            (+)
                            (
                                sum [2 \\ [v: _] <- i| 0 <= v]
                                + sum (
                                    zipWith
                                        (
                                            \[_, _, _, c: _] [_, _, j: _] = j - c
                                        )
                                        t
                                        (
                                            takeWhile (\[v: _] = v < 0) (tl t)
                                        )
                                ) * (1 - sign (v + 1))
                                - x!!1
                            )
                        )
                        x
                ]
            \\ i <- inits p
            &  t <- tails p
            &  [v, r: x] <- p
            ]
        )
        (concatMap @ l)
    #g = [g \\ [_, 2, g: _] <- p]
    =[[-1, 0, (hd g + last g)/2: g]: p];
@ (I i) = [[i, 0, 0, 0]];

3

木炭127123字节

↶≔⟦⟦θ⟧⟧ηFη«≔⊟ιζ¿⁼Iζ⪫⟦ζ⟧ω⊞υ⊞OιζFLζ⊞η⁺ι⟦⊖Lζκ§ζκ⟧»Wυ«≔⌊υι≔Φυ¬⁼κιυJ±⊗Lυ⊘⊖LιI⊟ιWι«≔⊟ιζ¿ζ«←§┐┬‹ζ⊟ιW⁼KKψ←─≔⁰ι»¿⊟ι┌¶┴¦│

在线尝试!链接是详细版本的代码。说明:

由于我们没有在右侧绘制任何东西,因此将默认绘制方向更改为上。

≔⟦⟦θ⟧⟧η

所述第一步骤是嵌套数组表示转换成一个索引表示这是所有条目的列表连同子阵列,例如的指数对输入q=[1, [[[2, 3]], [4], 5]]5q[1][2]我们要的是,因此该列表1, 2。我们从一个要处理的条目开始,该条目是一个包含当前索引列表(即到目前为止没有索引)和原始输入的列表。

Fη«

在处理数组时循环遍历它们。(方便地,如果在迭代过程中将木炭推入列表,木炭将继续遍历该列表。)

≔⊟ιζ

获取下一个要处理的数组。

¿⁼Iζ⪫⟦ζ⟧ω

这实际上是标量而不是数组吗?

⊞υ⊞Oιζ

如果是这样,那么我们所拥有的列表实际上就属于索引列表的最终列表。

FLζ

否则,循环遍历此数组中的每个元素...

⊞η⁺ι⟦⊖Lζκ§ζκ⟧»

...并将其保存到新的索引列表中,以供进一步处理。还将保存数组的最大索引,该索引用于特殊情况下数组的最后一个元素。

Wυ«

现在,我们准备遍历索引列表的列表。但是,该列表不是按字典顺序排列的,因此我们无法直接对其进行迭代。

≔⌊υι

按字典顺序查找下一个元素。

≔Φυ¬⁼κιυ

从列表中删除它。

J±⊗Lυ⊘⊖Lι

跳至输出中标量的位置。我们可以计算此值,因为我们可以保留输出的标量数量的计数,并且还知道其索引列表中的条目数。

I⊟ι

实际打印标量。

Wι«

循环访问索引列表中的条目。同样,这不是简单的迭代,因为条目成对出现,而且我们还需要能够突破循环。

≔⊟ιζ

从列表中提取下一个索引。

¿ζ«

如果这不是列表中的第一个元素...

←§┐┬‹ζ⊟ι

...然后打印根据这是否是列表中的最后一个元素...

W⁼KKψ←─

...并打印足够的s来填充此级别的上一个条目...

≔⁰ι»

...并清除变量以使之脱离循环,因为我们在这里已经完成。

¿⊟ι┌¶┴

否则,如果这是(多个元素的)列表的第一个元素,则打印┌┴,将光标留在之上以处理该级别的父级。

¦│

否则,如果这是一个1元素的列表,则只需打印a 并向上移动一行以处理该级别的父级。

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.