Stampa un albero binario


18

Ispirato da una recente domanda su SO ...

Scrivi una funzione per stampare un albero binario nel seguente formato:

   3
 /   \
1     5
 \   / \
  2 4   6
  1. L'output dovrebbe essere costituito da una linea di nodi, seguita da una linea di /e \caratteri che indicano le relazioni, seguita da una linea di nodi, ecc.
  2. Puoi presumere che tutti i nodi siano rappresentabili come un singolo carattere.
  3. I nodi adiacenti al livello più basso dovrebbero essere separati da almeno uno spazio, i nodi più in alto dovrebbero essere separati come appropriato.
  4. I nodi con due bambini dovrebbero essere posizionati esattamente nel mezzo dei loro figli diretti.
  5. Le barre delle relazioni dovrebbero essere a metà strada tra il genitore e il figlio appropriato (arrotondare in qualunque modo desiderato).

Ingresso:

L'input verrà fornito come argomento per la tua funzione. Non specificherò l'esatta struttura dell'albero, tuttavia deve essere utilizzabile come un vero albero binario. No "gli alberi sono rappresentati nel mio programma come stringhe che coincidono casualmente come l'output previsto".

È possibile stampare su un flusso di output o restituire una stringa contenente l'output, a scelta.

Punti per il codice più breve, ma preferirei di gran lunga una soluzione lunga perfettamente funzionante rispetto a una breve del 90% funzionante.


Aggiornamento per la taglia:

Per la taglia, io (ottimizzatore) sto apportando lievi modifiche:

  • L'input può provenire da STDIN, ARGV o argomento della funzione.
  • L'output deve essere su STDOUT (o console.logper JS)
  • Puoi supporre che l'input sia in una forma di array, ad esempio. [1,2,3]o[1 2 3]

Aggiornamento 2 : l'albero binario dovrebbe effettivamente essere un albero di ricerca binario. Dato che non ne ho parlato inizialmente, consentirò agli utenti di trattare la conversione di un array normale in un array di alberi di ricerca binario come un programma separato e il conteggio dei byte finale sarà solo per il programma di prendere l'array come argomento e stamparlo come un albero binario.


Dovremmo usare diverse barre delle relazioni appropriate? Dobbiamo usare il numero minimo di barre? Si dovrebbe distinguere tra avere un figlio unico sinistro e un figlio unico destro? Andrebbe bene avere spazi iniziali in ogni linea di uscita?

Cosa facciamo se l'albero non è completo (2 ^ n-1 nodi per alcuni n)? Alcuni nodi (quali?) Hanno un solo figlio. Ma se ci è permesso avere nodi con un solo figlio, l'albero degenerato è facile da fare (1-2-3-4-5-6 in basso e a destra, diciamo).
Keith Randall,

Come lo disegni per grandi numeri? Ad esempio30000,1000,499999
Mohsen il

Risposte:


9

Fortran 77-1085 caratteri

      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

L'albero è rappresentato nella matrice di input tnel solito modo, radice a 1, radice-> sinistra a 2, radice-> destra a 3 radice-> sinistra-> sinistra a 4 ...

L'uscita dovrebbe adattarsi a un terminale convenzionale fino a 5 livelli di profondità.

Uso esattamente una barra tra ogni coppia di nodi, che sembra piuttosto sciocca vicino alla cima una volta che ci sono quattro o più livelli. Ho consentito per un massimo di tre nodi di cifre.

Programma completo con commenti e un'impalcatura di lancio:

      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

Uscita con input equivalente all'esempio:

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

AIUTO perché questa lingua?
tomsmeding,

9
Perché è così poco adatto al golf.
dmckee,

5

CJam, 100 99 byte

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];

L'input deve essere un elenco di caratteri, senza caratteri di controllo ASCII. I nodi vuoti sono indicati da uno spazio. Deve anche essere un albero binario perfetto con esattamente 2 n -1 nodi.

Esempio:

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

O semplicemente usa le stringhe:

"63714 902 5  8 "

Produzione:

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

Spiegazione

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. ";

Script di conversione

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

Accetta caratteri o numeri a una cifra.

Esempi (tutti uguali):

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

Produzione:

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

È una semplice costruzione di alberi cartesiani.


Puoi semplicemente aggiungere altri due byte e inserire l'input dello script di conversione come numeri interi anziché caratteri :)
Ottimizzatore

@Optimizer Modificato per supportare entrambi. Penso che i personaggi abbiano più senso poiché supportano solo i nomi dei nodi con un singolo personaggio. Ci sono molti più caratteri rispetto ai numeri a una cifra.
jimmy23013,

2

Python 2, 411 byte

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:])

Nota: il primo livello di rientro è 1 spazio, il secondo è una scheda.

Chiama fcon un elenco di stringhe di un carattere o Nonedi, ad es.f(['1',None,'3']). L'elenco non può essere vuoto.

Questo dovrebbe obbedire alle regole per la generosità.

Script del convertitore:

Converte e array nel formato utilizzato dalla stampante ad albero binario. Esempio:

$ 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

Esempi:

Per eseguirli dovresti avere il nome del file principale bt.pye il file del convertitore denominato 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

In realtà non stai creando l'albero binario. Basta stampare l'array come un albero binario. L'output di ['1','2','3','4','5','6','7','8','9']array non è quello che hai mostrato. Dovrebbe avere 3come figlio 2giusto il figlio giusto di 1cui è un elemento radice.
Ottimizzatore

@Optimizer Dalla domanda: "L'input verrà fornito come argomento per la tua funzione. Non specificherò la struttura esatta dell'albero, tuttavia deve essere utilizzabile come un vero albero binario." Non vedo una definizione specifica del formato dell'array utilizzato.
Tyilo,

La domanda originariamente riguarda la stampa di un albero binario . Le tue uscite non sono alberi binari. Il formato dell'array non ha nulla a che fare con esso.
Ottimizzatore

@Optimizer come non sono alberi binari? Da Wikipedia: un albero binario è una struttura di dati ad albero in cui ogni nodo ha al massimo due figli. Qualcuno dei nodi ha più di due figli?
Tyilo,

Ughh. Ora vedo. Penso che ci sia un termine malinteso qui. Anche negli esempi iniziali, l'output è nel formato dell'albero di ricerca binario . E la mia taglia è anche solo per un albero di ricerca binario. Ci scusiamo per la confusione lì.
Ottimizzatore

1

APL, 125 caratteri

{⍵{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(×,*)≡⍵}

Esempio:

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

Testato qui.


Anche questo è lo script di conversione?
Ottimizzatore

@Optimizer Prende il formato di input dell'array nidificato, che può essere probabilmente utilizzato come albero di ricerca binario (ma non sono sicuro della complessità). Se devo usare alcuni formati più comuni ... forse lo farò più tardi.
jimmy23013,

@Optimizer Ora leggendo di nuovo la domanda, "array di alberi di ricerca binari" significa l'array di un albero binario completo in ordine di profondità (o qualcos'altro)? Non pensavo fosse qualcosa di specifico. E la ricerca di questo termine non ha dato nulla di utile.
jimmy23013,


@Optimizer Beh, era proprio quello che intendevo. Ma non penso che di solito sia chiamato "array di alberi di ricerca binari", ma solo "una sorta di archiviazione di array di alberi binari". Probabilmente ha bisogno di qualche chiarimento ... E probabilmente riparerò questa risposta giorni dopo, forse in un'altra lingua ...
jimmy23013

0

Rubino, 265 byte

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

La versione @proudhaskeller, 269 byte

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

spiegazione

La versione dettagliata:

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

Esempio

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
])

dà:

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

(Non ho ancora scritto lo script di conversione.)


le tue barre non sono esattamente nel mezzo
orgoglioso haskeller il

@proudhaskeller "tondo in qualunque modo tu voglia", ho pensato che fosse più bello in questo modo. Se lo desideri, puoi sostituire s / 2 con (s + 1) / 2.
AlexRath,

No, le barre nella prima riga non sono esattamente al centro, in questa riga non si tratta di arrotondare
orgoglioso haskeller il

@proudhaskeller Se sostituisci s / 2 con (s + 1) / 2 sono esattamente nel mezzo, ma preferisco ancora così perché fa apparire i rami più a sinistra e più a destra.
AlexRath,

è contro le specifiche ...
orgoglioso haskeller il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.