Рисование дерева из массива

24

Учитывая возможно вложенный, непустой массив однозначных положительных целых чисел (не гарантировано уникальных), выведите представление ASCII-art в виде дерева, используя символы рисования блоков ┌ ┴ ┐ ─ │ ┬ ┼. (Они были скопированы из кодовой страницы 437, но вы можете использовать любое эквивалентное представление).

Каждое целое число массива должно быть листом дерева. Элементы одного уровня глубоко в массиве должны присутствовать на одном уровне дерева. Все элементы должны быть отделены друг от друга достаточным количеством пробелов, чтобы их можно было различить (до вас зависит, какова ширина, минимум одного пробела между ними).

Например, для данного массива [[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 или вернуть как результат функции.
  • Либо полная программа или функция приемлемы.
  • Любое количество посторонних пробелов является приемлемым, при условии, что символы выстраиваются соответствующим образом.
  • Стандартные лазейки запрещены.
  • Это поэтому применяются все обычные правила игры в гольф, и выигрывает самый короткий код (в байтах).
AdmBorkBork
источник
[1,[[[2,3],4],5]]Это может быть интересным тестовым примером, поскольку ему нужно искусственно расширить корень, чтобы правое поддерево не сталкивалось с левым поддеревом.
Тыкни
@Poke Добавлен в качестве примера. Есть несколько возможных вариантов для этого теста.
AdmBorkBork
2
Первый пример для этого теста не может быть правильным. Это говорит о том, что второй по элементу рядом с 1представляет собой массив из 3 -х элементов: [2,3], 4, и 5. Но 4 и 5 не соседствуют.
Draco18s
4
Это похоже [1, [[[2, 3]], [4], 5]]на меня.
Нил
Какой (если таковой имеется) из этих альтернативных форматов ввода будет приемлемым?
Οurous

Ответы:

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: обрезать 7 байтов, избегая дублирования ┴┼(чистое сохранение 2 байта), вырезая 0 из одной строки, изменяя способ выбора символов рисования ┬┌┐(используйте <вместо ==) и заменяя L(z)пропущенный I наe

РЕДАКТИРОВАТЬ 2: -2 байта благодаря ovs и -1 байту благодаря Kevin Cruijssen

Попробуйте онлайн!

Ungolfed

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]

Строит дерево из листьев, по одному слою за раз.

Beefster
источник
2
Я добавил тестовые примеры к вашей ссылке TIO. Попробуйте онлайн!
pizzapants184
Хороший ответ! Вы можете сократить это два байта, назначая место для переменной , как это: S,*K=' ┴┼│123456789'.
овс
1
e==1может быть, e<2чтобы сохранить байт (я не думаю, что это когда-либо может быть 0, так как вызов заявляет, что вход не пустой - и пустые входные данные max(map(L,z))в этом случае уже потерпели бы неудачу .)
Кевин Круйссен
3

Чисто , 544 506 байт

Экранирование используется, чтобы избежать недопустимого UTF-8 в SE / TIO, но считается одним байтом, поскольку они являются действительными литералами

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]];
Οurous
источник
3

Древесный уголь , 127 123 байта

↶≔⟦⟦θ⟧⟧η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η«

Зацикливайтесь на массивах по мере их обработки. (Удобно, что Charcoal будет продолжать перебирать список, если вы нажмете на него во время итерации.)

≔⊟ιζ

Получить следующий массив для обработки.

¿⁼Iζ⪫⟦ζ⟧ω

Это на самом деле скаляр, а не массив?

⊞υ⊞Oιζ

Если так, то список, который у нас был, фактически принадлежит к окончательному списку списков индексов.

FLζ

В противном случае, цикл по каждому элементу в этом массиве ...

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

... и сохраните его с новым списком индексов для дальнейшей обработки. Также сохраняется максимальный индекс массива, который используется для особого случая последнего элемента массива.

Wυ«

Теперь мы готовы перебрать список индексов. Однако список не в лексикографическом порядке, поэтому мы не можем повторить его напрямую.

≔⌊υι

Найти следующий элемент в лексикографическом порядке.

≔Φυ¬⁼κιυ

Удалить его из списка.

J±⊗Lυ⊘⊖Lι

Перейти к положению скаляра на выходе. Мы можем рассчитать это, учитывая, что мы можем вести подсчет количества скаляров, которые мы выводим, и мы также знаем количество записей в его индексном списке.

I⊟ι

На самом деле распечатать скаляр.

Wι«

Цикл по записям в списке индексов. Опять же, это не простая итерация, потому что записи идут парами, и мы также должны иметь возможность выйти из цикла.

≔⊟ιζ

Извлеките следующий индекс из списка.

¿ζ«

Если это не первый элемент в списке ...

←§┐┬‹ζ⊟ι

... затем распечатать или в зависимости от того, является ли это последним элементом в списке ...

W⁼KKψ←─

... и выведите достаточно s, чтобы заполнить предыдущую запись на этом уровне ...

≔⁰ι»

... и очистите переменную, чтобы выйти из цикла, так как мы здесь закончили.

¿⊟ι┌¶┴

В противном случае, если это (первый элемент) многоэлементный список, выведите его ┌┴, оставив курсор над, чтобы иметь дело с родителем этого уровня.

¦│

В противном случае, если это список из 1 элемента, просто выведите a и переместите строку вверх, чтобы иметь дело с родителем этого уровня.

Нил
источник