Platinum2d

Binary tree print on a console application

Jun 7th, 2017
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 12.64 KB | None | 0 0
  1. //basic.h
  2.  
  3. #ifndef BASIC_H
  4. #define BASIC_H
  5. #define _CRT_SECURE_NO_WARNINGS
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <stdbool.h>
  9. #include <stdint.h>
  10. #include <math.h>
  11. #include <string.h>
  12.  
  13. typedef int element;
  14.  
  15. typedef struct item {
  16.     element value;
  17.     struct item *left, *right;
  18. } node;
  19.  
  20. typedef node* tree;
  21.  
  22. extern bool empty(tree t);
  23. extern tree emptyTree();
  24. extern element root(tree t);
  25. extern tree left(tree t);
  26. extern tree right(tree t);
  27. extern void showElement(element e);
  28. extern tree consTree(element e, tree l, tree r);
  29. extern bool isLess(element lhs, element rhs);
  30. extern bool isEqual(element lhs, element rhs);
  31. extern bool isGreater(element lhs, element rhs);
  32. extern void destroyTree(tree t);
  33. extern void preOrder(tree t);
  34. extern void postOrder(tree t);
  35. extern void inOrder(tree t);
  36. extern bool booleanSearch(element e, tree t);
  37. extern uint32_t height(tree t);
  38. extern void getElementsAtLevel(uint32_t level, tree t, uint32_t k, tree *v, uint32_t *pos);
  39. extern void printTree(tree t, int level, const tree start, FILE *f);
  40. extern uint32_t getLeftSpaces(uint32_t h);
  41. extern uint32_t getDigitsNumber(int n);
  42.  
  43. #endif // !BASIC_H
  44.  
  45. //basic.c
  46.  
  47. bool isEqual(element lhs, element rhs)
  48. {
  49.     return lhs == rhs;
  50. }
  51.  
  52. bool isLess(element lhs, element rhs)
  53. {
  54.     return lhs < rhs;
  55. }
  56.  
  57. bool isGreater(element lhs, element rhs)
  58. {
  59.     return !isLess(lhs, rhs) && !isEqual(lhs, rhs);
  60. }
  61.  
  62. bool empty(tree t)
  63. {
  64.     return (t == NULL);
  65. }
  66.  
  67. tree emptyTree()
  68. {
  69.     return NULL;
  70. }
  71.  
  72. element root(tree t)
  73. {
  74.     if (empty(t)) abort();
  75.     return t->value;
  76. }
  77.  
  78. tree left(tree t)
  79. {
  80.     if (empty(t)) abort();
  81.     return t->left;
  82. }
  83.  
  84. tree right(tree t)
  85. {
  86.     if (empty(t)) abort();
  87.     return t->right;
  88. }
  89.  
  90. tree consTree(element e, tree l, tree r)
  91. {
  92.     tree t = malloc(sizeof(node));
  93.     t->value = e;
  94.     t->left = l;
  95.     t->right = r;
  96.  
  97.     return t;
  98. }
  99.  
  100. void showElement(element e)
  101. {
  102.     printf("%d", e);
  103. }
  104.  
  105. void preOrder(tree t)
  106. {
  107.     if (!empty(t))
  108.     {
  109.         printf("\t");
  110.         showElement(root(t));
  111.         preOrder(left(t));
  112.         preOrder(right(t));
  113.     }
  114. }
  115.  
  116. void postOrder(tree t)
  117. {
  118.     if (!empty(t))
  119.     {
  120.         postOrder(left(t));
  121.         postOrder(right(t));
  122.         printf("\t");
  123.         showElement(root(t));
  124.     }
  125. }
  126.  
  127. void inOrder(tree t)
  128. {
  129.     if (!empty(t))
  130.     {
  131.         inOrder(left(t));
  132.         printf("\t");
  133.         showElement(root(t));
  134.         inOrder(right(t));
  135.     }
  136. }
  137.  
  138. bool booleanSearch(element e, tree t)
  139. {
  140.     if (!empty(t)) return false;
  141.  
  142.     if (isEqual(root(t), e)) return true;
  143.     else
  144.         return (booleanSearch(e, left(t)) || booleanSearch(e, right(t)));
  145. }
  146.  
  147. void destroyTree(tree t)
  148. {
  149.     if (!empty(t))
  150.     {
  151.         tree l = left(t), r = right(t);
  152.         free(t);
  153.         destroyTree(l); destroyTree(r);
  154.     }
  155. }
  156.  
  157. uint32_t getDigitsNumber(int n)
  158. {
  159.     uint32_t digits = 1;
  160.     while ((n /= 10) != 0) digits++;
  161.  
  162.     return digits;
  163. }
  164.  
  165. uint32_t height(tree t)
  166. {
  167.     if (empty(t)) return 0;
  168.     else
  169.     {
  170.         uint32_t hl = height(left(t)),
  171.             hr = height(right(t));
  172.         return 1 + (hl>hr ? hl : hr);
  173.     }
  174. }
  175.  
  176. /*
  177. Esiste una relazione matematica tra il numero di spazi stampati a sinistra del primo in ampiezza ad ogni livello dell'albero. Essa è descritta come
  178.  
  179. a(h) = 2 * a(h - 1) + 3, h € N U {0}
  180. con a(0) = 0 e h altezza del sottoalbero considerato.
  181. */
  182.  
  183. uint32_t getLeftSpaces(uint32_t h) //Implementazione della successione matematica sopra citata.                                                                        
  184. {
  185.     uint32_t ris = 0;
  186.  
  187.     for (size_t i = 0; i < h; i++)
  188.         ris = 2 * ris + 3;
  189.  
  190.     return ris;
  191. }
  192.  
  193. uint32_t getMidlleSpaces(uint32_t left_spaces) //Gli spazi da stampare tra un sottoalbero e l'altro sono gli stessi stampati a sinistra del primo nodo, tenendo però conto delle stampe delle radici.
  194. {
  195.     return left_spaces + 2;
  196. }
  197.  
  198. void getElementsAtLevel(uint32_t level, tree t, uint32_t k, tree *v, uint32_t *pos) //level: livello di cui si desiderano i nodi (0 è considerato il livello della radice) | t : puntatore alla radice dell'intero albero | k : contatore per tenere traccia del numero di livelli attraversati | *v : successione contenente i nodi trovati (si suppone sia già stata allocata nella funzione chiamante) | *pos : posizione corrente in cui inserire un elemento.
  199. {
  200.     if (!empty(t) && k <= level)                                                    //Se il valore corrente di t è analizzabile e si su un livello precedente o uguale a quello desiderato, si distingue in due casi:
  201.     {
  202.         if (k == level)                                                             //Primo caso: il nodo preso in analisi è sul livello che si desidera.
  203.         {
  204.             v[*pos] = t;                                                            //Il nodo viene messo nella posizione di inserimento corrente, assieme a tutta la struttura sottostante ("l'oggetto" tree t). Non viene effettuato alcun controllo su *pos in quanto, data la struttura dell'albero binario, è matematicamente certo che sarà sempre minore di 2^level.
  205.             (*pos)++;                                                               //La corretta posizione in cui inserire adesso è la prossima.
  206.             return;
  207.         }
  208.         getElementsAtLevel(level, left(t), k + 1, v, pos);                          //Secondo caso: il nodo è a un livello minore (visivamente risulta più alto, più vicino alla radice) di quello desiderato (non potrà mai essere maggiore per quanto detto sopra sulle proprietà matematiche degli alberi binari).
  209.         getElementsAtLevel(level, right(t), k + 1, v, pos);                         //Vengono quindi cercati i nodi al livello k+1, andando nel sottoalbero sinistro e destro di ognuno.
  210.     }
  211.     else
  212.     {
  213.         (*pos) += (uint32_t)pow(2, level - k);                                      //Se il controllo soprastante è fallito, allora bisogna spostare *pos di tante posizioni quante sono quelle "perse", ovvero quelle in cui non sono presenti nodi dello stesso livello.
  214.                                                                                     //Attenzione: le posizioni da saltare nel vettore v non sono sempre 1. Se si incontra un nodo NULL che però è ad un livello maggiore di quello desiderato (caso k < level, con "maggiore" si intende da un punto di vista visivo), le posizioni perse sono tante quante i sottonodi di livello k che quel nodo avrebbe potuto avere, ovvero 2^(level - k). Viene quindi incrementato *pos di tale valore.
  215.     }
  216. }
  217.  
  218. void printTree(tree t, int level, const tree start, FILE *f)                                                    //t : albero da stampare | level = 0 : livello corrente da stampare all'intero della procedura ricorsiva | start: albero di partenza, necessario alle funzioni asuliari.
  219. {
  220.     if (empty(t)) return;                                                                                       //Condizione di uscita dalla ricorsione.
  221.     tree *nodes = malloc((uint32_t)pow(2, level) * sizeof(tree*));                                              //Essendo tree un puntatore a item, nodes è un puntatore a puntatore. Serve ad immagazzinare una successione di cardinalità 2^level (numero di nodi al level-esimo livello dell'albero), contenente dei tree aventi come radici i nodi di livello level.
  222.     memset(nodes, NULL, (uint32_t)pow(2, level) * sizeof(tree*));                                               //Motivo principale per cui nodes è di tipo tree*. Rimarranno NULL tutte quelle posizioni in cui il nodo non c'è. Questa operazione è eseguita da getElementsAtLevel.
  223.  
  224.     uint32_t appoggio = 0;                                                                                      //Variabile che serve a getElementsAtLevel per riempire nodes solo nelle posizioni che, in ampiezza, non sono NULL.
  225.     getElementsAtLevel(level, start, appoggio, nodes, &appoggio);                                               //Acquisizione dei nodi al livello level.
  226.  
  227.     int h = height(t) - 1;                                                                                      //height() restituisce il livello massimo dell'albero, considerando la radice di livello 1 (e non 0). Per usare il valore nelle funzioni ausiliari che seguono, basta scalarlo di 1.
  228.     uint32_t left_spaces = getLeftSpaces(h);
  229.     int left_spaces_next, middle_spaces;
  230.  
  231.     left_spaces_next = h > 0 ? getLeftSpaces(h - 1) : -1;                                                       //Vengono stabiliti gli spazi a sinistra del livello sottostante quello corrente. Non ha però senso calcolarli per h = 0 (significa che quello raggiunto è l'ultimo livello), quindi viene impostato -1 per garantire la fine immediata dei cicli sottostanti.
  232.     middle_spaces = getMidlleSpaces(getLeftSpaces(h + 1));                                                      //Vengono stabiliti gli spazi da stampare tra un numero e l'altro.
  233.  
  234.     for (size_t i = 0; i < left_spaces; i++) fprintf(f, " ");                                                   //Vengono stamparti gli spazi a sinistra, ma solo se si sta stampando il primo nodo in ampiezza (i restanti spazi tra un nodo e l'altro sono sempre spazi.
  235.     for (size_t repeat = 0; repeat < pow(2, level); repeat++)                                                   //Questo ciclo occorre quando è necessario ripetere le stesse operazioni per ogni nodo nel livello correntemente analizzato dell'albero binario.
  236.     {
  237.         uint32_t distance_for_digits = 0;                                                                       //Vengono quindi stampati tutti i nodi (esattamente 2^level) e ognuno viene messo nella giusta posizione.                                                                                                      
  238.         if (!empty(nodes[repeat]))
  239.         {
  240.             fprintf(f, "%i", root(nodes[repeat]));
  241.             uint32_t digits = getDigitsNumber(root(nodes[repeat]));
  242.             if (digits > 1) distance_for_digits = digits - 1;                                                   //Gestione molto rudimentale dei numeri a più cifre
  243.         }
  244.         else
  245.             fprintf(f, " ");                                                                                    //Viene stampato il nodo solamente se c'è, altrimenti deve essere stampato uno spazio per mantenere intatta la restante struttura dell'albero.
  246.         for (int i = 0; i < middle_spaces - distance_for_digits; i++) fprintf(f, " ");                          //Stampa degli spazi tra un sottoalbero e l'altro.
  247.     }
  248.  
  249.     fprintf(f, "\n");                                                                                           //Fine della riga in cui sono presenti tutti i nodi del level-esimo livello.
  250.     left_spaces--;                                                                                              //Viene fatta manualmente la modifica del numero di spazi a sinistra del primo nodo e tra i simboli / e \ alla prima iterazione.
  251.     middle_spaces -= 2;                                                                                         //Gli spazi tra / e \ decrementano ad ogni iterazione a 2 a 2.
  252.  
  253.     for (int i = left_spaces, m = 1; i > left_spaces_next; i--, m += 2, middle_spaces -= 2)                     //finchè il numero di spazi a sinistra da stampare non è quello del livello sottostante (spaces_left_next), il ciclo esegue le operazioni contenute, rispettando quanto affermato sopra riguardo il numero di spazi a sinistra e in mezzo. Leggere sotto per capire l'utilità di ogni variabile indice.
  254.     {
  255.         for (int j = 0; j < i; j++) fprintf(f, " ");                                                            //Stampa degli spazi a sinistra attuali (i).
  256.         for (size_t repeat = 0; repeat < pow(2, level); repeat++)
  257.         {
  258.             if (!empty(nodes[repeat]) && !empty(left(nodes[repeat]))) fprintf(f, "/"); else fprintf(f, " ");    //stampa l'arco sinistro solo se il nodo non è NULL e se l'arco sinistro esiste (ovvero se il figlio sinistro esiste). Altrimenti viene stampato uno spazio per non alterare il resto della struttura.
  259.             for (int j = 0; j < m; j++) fprintf(f, " ");                                                        //L'indice m serve per stampare gli spazi tra i simboli / e \ ad ogni riga, che sono differenti da quelli tra un sottoalbero e l'altro (middle_spaces)
  260.             if (!empty(nodes[repeat]) && !empty(right(nodes[repeat]))) fprintf(f, "\\"); else fprintf(f, " ");  //Operazione perfettamente speculare a quella descritta due righe sopra.
  261.             for (int i = 0; i < middle_spaces; i++) fprintf(f, " ");                                            //Stampa degli spazi tra sottoalberi attuali.
  262.         }                                                                                                       //Viene decrementato il numero di spazi tra / e \ (m), tra sottoalberi (middle_spaces) e a sinistra (i)
  263.         fprintf(f, "\n");                                                                                       //Fine riga.
  264.     }
  265.  
  266.     uint32_t lh = !empty(left(t)) ? height(left(t)) : 0;                                                        //L'albero viene attraversato in profondità per garantire alle funzioni ausiliarie di passare di ricavare tutti i nodi in ampiezza di livello in livello. Questo significa però che la chiamata ricorsiva in profondità deve considerare il sottoalbero di altezza maggiore, sui cui la funzione height() aveva basato i propri calcoli. Se così non fosse e venisse preso il sottoalbero di sinistra o destra in modo arbitrario, l'algoritmo non funzionerebbe in tutti i casi, in quanto potrebbe essere preso il sottoalbero più corto tra i due, quindi il calcolo dell'altezza nella chiamata ricorsiva successiva sarebbe errato.
  267.     uint32_t rh = !empty(right(t)) ? height(left(t)) : 0;                                                       //Vengono dunque prese in considerazione le altezza dei due sottoalberi....
  268.  
  269.     lh > rh ? printTree(left(t), level + 1, start, f) : printTree(right(t), level + 1, start, f);               //... E la chiamata ricorsiva in profondità avviene solo sul sottolbero più alto.
  270.  
  271.     for (size_t i = 0; i < pow(2, level); i++) free(nodes[i]);                                                  //Per risparmiare (poca) memoria vengono liberati solo i puntatori allocati. Liberare anche ciò a cui puntano questi ultimi significherebbe deallocare tutto l'albero su cui si sta lavorando.
  272. }
  273.  
  274. int main(void)
  275. {
  276.     tree t = consTree(1, consTree(2, consTree(4, consTree(4, NULL, NULL), consTree(5, NULL, NULL)), consTree(4, consTree(4, NULL, NULL), consTree(4, NULL, NULL))), consTree(4, consTree(5, consTree(6, NULL, NULL), consTree(4, NULL, NULL)), consTree(2, consTree(4, NULL, NULL), consTree(7, NULL, consTree(7, NULL, consTree(7, NULL, NULL))))));
  277.     printf("\n");
  278.  
  279.     FILE *ft = fopen("tree.txt", "wt");
  280.  
  281.     printTree(t, 0, t, ft);
  282.    
  283.     fclose(ft);
  284.     return EXIT_SUCCESS;
  285. }
Advertisement
Add Comment
Please, Sign In to add comment