Роздрукуйте двійкове дерево


18

Натхненний останнім запитанням про SO ...

Напишіть функцію для друку двійкового дерева у такому форматі:

   3
 /   \
1     5
 \   / \
  2 4   6
  1. Вихід повинен складатися з рядка вузлів, за яким слід рядка /та \символів, що вказують взаємозв'язки, а потім лінії вузлів тощо.
  2. Можна припустити, що всі вузли представлені як один символ.
  3. Суміжні вузли на найнижчому рівні повинні бути розділені щонайменше одним пробілом, вузли далі вгору повинні бути відокремлені відповідно.
  4. Вузли з двома дітьми повинні розміщуватися саме посередині їхніх прямих дітей.
  5. Штрихи для відносин повинні бути на півдорозі між батьком та відповідною дитиною (кругло, де ви хочете).

Вхід:

Вхід буде надано як аргумент вашої функції. Я не вказуватиму точну структуру дерева, однак воно повинно бути використане як власне бінарне дерево. Жодні "дерева не представлені в моїй програмі, оскільки рядки збігаються збігом випадків, як очікуваний вихід".

Ви можете надрукувати на вихідний потік або повернути рядок, що містить вихід, на ваш вибір.

Бали за найкоротший код, але я б більше віддав перевагу повністю працюючому довгому рішенню, ніж короткий короткий на 90%.


Оновлення для винагороди:

Щодо щедрості, я (оптимізатор) вношу незначні зміни:

  • Вхід може бути з STDIN, ARGV або аргументу функції.
  • Вихідні дані повинні бути STDOUT (або console.logдля JS)
  • Можна припустити, що введення знаходиться у вигляді масиву, наприклад, [1,2,3]або[1 2 3]

Оновлення 2 - Бінарне дерево насправді має бути двійковим деревом пошуку. Оскільки я не згадував про це спочатку, я дозволю користувачам трактувати перетворення звичайного масиву у масив двійкового пошуку дерева як окрему програму, і остаточне число байтів буде лише для програми, яка приймає масив як аргумент та друкує його як бінарне дерево.


Чи повинні ми використовувати кілька косої риски відносин, які були доречними? Чи повинні ми використовувати мінімальну кількість косої риски? Чи слід розрізняти народження однієї лівої дитини та єдиної правої дитини? Було б добре мати провідні пробіли у кожному вихідному рядку?

Що робити, якщо дерево не є повним (2 ^ n-1 вузлів для деякого n)? У деяких вузлах (які?) Мають лише одну дитину. Але якщо нам дозволено мати вузли лише з однією дитиною, вироджене дерево легко зробити (скажімо, 1-2-3-4-5-6 вниз та праворуч).
Кіт Рендалл

Як ви малюєте це для великої кількості? Наприклад30000,1000,499999
Мохсен

Відповіді:


9

Фортран 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, корінь-> праворуч у 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

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 Відредаговано для підтримки обох. Я думаю, що символи мають більше сенсу, оскільки вони підтримують лише імена вузлів з одним символом. Знаків значно більше, ніж одноцифрових чисел.
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зі списком рядків з одним символом або Nones, наприклад, 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: як вони не двійкові дерева? З Вікіпедії: двійкове дерево - це структура даних про дерево, в якій кожен вузол має щонайбільше двох дітей. У будь-якого з вузлів більше двох дітей?
Тайло

Тьфу. Я зараз бачу. Я думаю, що тут є термін непорозуміння. Навіть у початкових прикладах висновок має формат бінарного дерева пошуку . І моя щедрість є також лише для двійкового дерева пошуку. Вибачте за плутанину там.
Оптимізатор

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 Він приймає вкладений формат введення масиву, який, ймовірно, може використовуватися як двійкове дерево пошуку (але я не впевнений у складності). Якщо мені доведеться скористатися більш звичними форматами ... можливо, я це зроблю пізніше.
jimmy23013

@Optimizer Тепер, читаючи питання ще раз, чи означає "двійковий масив дерева пошуку" масив повного бінарного дерева в порядку глибини (чи щось інше)? Я не думав, що це щось конкретне. І пошук цього терміна не дав нічого корисного.
jimmy23013


@Optimizer Ну, це було саме те, що я мав на увазі. Але я не думаю, що його зазвичай називають «двійковим масивом дерева пошуку», а лише «своєрідним сховищем масиву бінарних дерев». Ймовірно, потрібне уточнення ... І я, мабуть,
виправлю

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

@proudhaskeller "кругле, куди хочете", я думав, що це виглядає крутіше. Ви можете замінити s / 2 на (s + 1) / 2, якщо хочете.
AlexRath


@proudhaskeller Якщо ви заміните s / 2 на (s + 1) / 2, вони знаходяться точно посередині, але я все одно віддаю перевагу цьому способу, оскільки він робить крайні ліві та праві праві гілки.
AlexRath

це проти спец ...
гордий haskeller
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.