Laboratorio di Algoritmi e Strutture Dati



Documenti analoghi
Funzioni in C. Violetta Lonati

La struttura dati ad albero binario

Laboratorio di Programmazione 1. Docente: dr. Damiano Macedonio Lezione 18 31/03/2014

Gli array. Gli array. Gli array. Classi di memorizzazione per array. Inizializzazione esplicita degli array. Array e puntatori

Alberi binari di ricerca

Esempio: dest = parolagigante, lettere = PROVA dest (dopo l'invocazione di tipo pari ) = pprrlogvgante

Fondamenti di Informatica T-1, 2009/2010 Modulo 2 Prova d Esame 5 di Giovedì 15 Luglio 2010 tempo a disposizione 2h30'

Gestione dei File in C

Algoritmi di Ricerca. Esempi di programmi Java

Algoritmi e strutture dati. Codici di Huffman

3 - Variabili. Programmazione e analisi di dati Modulo A: Programmazione in Java. Paolo Milazzo

LINGUAGGI DI PROGRAMMAZIONE

Definire all'interno del codice un vettore di interi di dimensione DIM, es. int array[] = {1, 5, 2, 4, 8, 1, 1, 9, 11, 4, 12};

Algoritmi e Strutture Dati & Laboratorio di Algoritmi e Programmazione

LABORATORIO DI PROGRAMMAZIONE EDIZIONE 1, TURNO B

Alberi binari di ricerca

CREAZIONE DI UN DATABASE E DI TABELLE IN ACCESS

Strutture. Strutture e Unioni. Definizione di strutture (2) Definizione di strutture (1)

LISTE, INSIEMI, ALBERI E RICORSIONE

ESERCIZI DI PROBLEM SOLVING E COMPOSIZIONE DEI DIAGRAMMI DI FLUSSO per le classi terza

void funzioneprova() { int x=2; cout<<"dentro la funzione x="<<x<<endl; }

Esercizi Capitolo 6 - Alberi binari di ricerca

Inizializzazione, Assegnamento e Distruzione di Classi

Corso di Informatica

Funzioni matlab per la gestione dei file. Informatica B Prof. Morzenti

Fondamenti di Informatica 2

Matlab: Gestione avanzata dei file

Quotazione compareto( ) Quotazione piurecente( ) Quotazione Quotazione Quotazione non trovato count( )

Strutturazione logica dei dati: i file

Struttura a record. File ad accesso diretto. Modalità di apertura. Modalità di apertura

Breve riepilogo della puntata precedente:

B+Trees. Introduzione

Codifica: dal diagramma a blocchi al linguaggio C++

Esercizi per il corso di Algoritmi e Strutture Dati

4 3 4 = 4 x x x 10 0 aaa

E possibile modificare la lingua dei testi dell interfaccia utente, se in inglese o in italiano, dal menu [Tools

5.3 TABELLE RECORD Inserire, eliminare record in una tabella Aggiungere record Eliminare record

(Esercizi Tratti da Temi d esame degli ordinamenti precedenti)

Matematica - SMID : Programmazione Febbraio 2009 FOGLIO RISPOSTE

INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI INTRODUZIONE AGLI ALGORITMI

Introduzione al Linguaggio C

Convertitori numerici in Excel

Le funzioni in C. I programmi C sono costituiti da definizioni di variabili e funzioni.

Programmazione dinamica

Leggere un messaggio. Copyright 2009 Apogeo

Aprire WEKA Explorer Caricare il file circletrain.arff Selezionare random split al 66% come modalità di test Selezionare J48 come classificatore e

RISOLUTORE AUTOMATICO PER SUDOKU

Le Macchine di Turing

Lezione 8. La macchina universale

Prova di Laboratorio di Programmazione

Introduzione alla programmazione in C

USO DI EXCEL CLASSE PRIMAI

Per scrivere una procedura che non deve restituire nessun valore e deve solo contenere le informazioni per le modalità delle porte e controlli

Informatica. Rappresentazione dei numeri Numerazione binaria

Uso di base delle funzioni in Microsoft Excel

Risolvere un problema significa individuare un procedimento che permetta di arrivare al risultato partendo dai dati

Editor vi. Editor vi

MATLAB. Caratteristiche. Dati. Esempio di programma MATLAB. a = [1 2 3; 4 5 6; 7 8 9]; b = [1 2 3] ; c = a*b; c

Variabili e tipi di dato

puntatori Lab. Calc. AA 2007/08 1

Appello di Informatica B

Programmazione C Massimo Callisto De Donato massimo.callisto@unicam.it

Le stringhe. Le stringhe

MANUALE UTENTE Fiscali Free

Concetto di Funzione e Procedura METODI in Java

Gestione dei File. dischi nastri cd

GUIDA RAPIDA PER LA COMPILAZIONE DELLA SCHEDA CCNL GUIDA RAPIDA PER LA COMPILAZIONE DELLA SCHEDA CCNL

I TUTORI. I tutori vanno creati la prima volta seguendo esclusivamente le procedure sotto descritte.

costruttori e distruttori

GESTIONE INFORMATICA DEI DATI AZIENDALI

Lab 11 Gestione file di testo"

INFORMATICA - I puntatori Roberta Gerboni

Università degli Studi di Cassino Corso di Fondamenti di Informatica Puntatori. Anno Accademico 2010/2011 Francesco Tortorella

I file di dati. Unità didattica D1 1

Appunti sulla Macchina di Turing. Macchina di Turing

Informatica B. Sezione D. Scuola di Ingegneria Industriale Laurea in Ingegneria Energetica Laurea in Ingegneria Meccanica

LABORATORIO DI PROGRAMMAZIONE 1 CORSO DI LAUREA IN MATEMATICA UNIVERSITÀ DEGLI STUDI DI MILANO V Indice

Analizzatore lessicale o scanner

OTTAVA ESPERIENZA DI LABORATORIO. L elaborazione dei files in C

Introduzione al linguaggio C Gli array

Cosa è un foglio elettronico

Esame del 3 febbraio 2010

I tipi di dato astratti

Uso di JUnit. Fondamenti di informatica Oggetti e Java. JUnit. Luca Cabibbo. ottobre 2012

risulta (x) = 1 se x < 0.

Allocazione dinamica della memoria - riepilogo

Università di Torino Facoltà di Scienze MFN Corso di Studi in Informatica. Programmazione I - corso B a.a prof.

10 - Programmare con gli Array

Capitolo 2. Operazione di limite

Dispensa YACC: generalità

Esempi di algoritmi. Lezione III

Appunti tratti dal videocorso on-line di Algoritmi e Programmazione Avanzata By ALeXio

Caratteri e stringhe Esercizi risolti

Testi di Esercizi e Quesiti 1

Moduli (schede compilabili) in Word Esempio: scheda di alimentazione per un degente

Plate Locator Riconoscimento Automatico di Targhe

Database Manager Guida utente DMAN-IT-01/09/10

I sistemi di numerazione

Rappresentazione delle informazioni

Transcript:

Laboratorio di Algoritmi e Strutture Dati Docente: Camillo Fiorentini 18 dicembre 2007 Esercizio 1: rappresentazione di una tabella di occorrenze L obiettivo è quello di rappresentare in modo efficiente una tabella di occorrenze, ossia una tabella che permette di contare le parole che compaiono in un testo. Formalmente, una occorrenza è una coppia word, num, dove word è una parola e num un intero positivo che rappresenta il numero di occorrenze di word. Operazioni Si richiede di implementare una struttura dati efficiente che permette di eseguire le seguenti operazioni. - addword(word) Se la parola word non è nella tabella, aggiunge alla tabella l occorrenza word, 1. Altrimenti incrementa di uno il numero di occorrenze di word. - addwordfromfile(f ilen ame) Se non esiste alcun file di nome filename stampa un messaggio di errore. Altrimenti, per ogni parola word nel file f ilen ame compie l operazione addword(word). - delword(word) Se la parola word è nella tabella, decrementa di uno il numero di occorrenze di word. Se il numero di occorrenze diventa 0, l occorrenza contenente word è tolta dalla tabella (è inutile mantenere nella tabella le coppie word, 0 ). - printocc(word) Stampa il numero di occorrenze della parola word. - printtab() Stampa il contenuto della tabella in ordine alfabetico (come nell esempio più avanti). Se la tabella è vuota, stampa un messaggio opportuno. - printtabinv() Stampa il contenuto della tabella in ordine alfabetico inverso (come nell esempio più avanti). Se la tabella è vuota, stampa un messaggio opportuno. Formato per l input e l output Il programma deve leggere da standard input una sequenza di linee, ciascuna delle quali corrisponde a una linea della prima colonna della tabella qui sotto, dove word è una parola e filename è il nome di un file. Si assume che tutte le parole abbiano al massimo WORD caratteri, dove il valore di WORD è definito nel programma. 1

Quando una linea è letta, viene eseguita l operazione associata. Le operazioni di stampa sono effettuate sullo standard output e ogni operazione deve iniziare su una nuova linea. Istruzione in input Operazione + word addword(word) r f ilen ame addwordfromfile(f ilen ame) - word delword(word)? word printocc(word) p printtab() i printtabinv() f termina l esecuzione Si assume che l input sia secondo il formato della tabella e che il file filename contenga delle parole aventi al massimo WORD caratteri separate da uno o più caratteri di spaziatura (spazi, a capo, ecc.). Struttura dati Occorre rappresentare un insieme di occorrenze su cui siano definite le usuali operazioni sugli insiemi dinamici (inserimento, cancellazione, ricerca, ecc.). Per rappresentare una occorrenza word, num usare la struttura struct occorrenza{ char* word; int num; }; typedef struct occorrenza occorrenza; Poiché tutte le operazioni (a parte le operazioni di stampa) richiedono la ricerca di una parola, occorre usare una struttura dati in cui la ricerca per nome sia efficiente. Utilizziamo un albero binario di ricerca dove: - L insieme V dei nodi è l insieme delle occorrenze word, num. - La relazione < sull insieme V è definita come segue word 1, num 1 < word 2, num 2 w 1 < l w 2 dove w 1 < l w 2 se e solo se w 1 è minore di w 2 rispetto all ordinamento lessicografico sulle parole. È facile verificare che < è un ordinamento lineare. 2

Per rappresentare un nodo dell albero usare la struttura: struct nodo { occorrenza* key; // indirizzo occorrenza rappresentata dal nodo struct nodo* up; struct nodo* left; struct nodo* right; }; typedef struct nodo nodo; Il campo key di un nodo dell albero di ricerca è l indirizzo dell occorrenza contenuta nel nodo; i campi up, left e right hanno il solito significato (indirizzo del nodi padre, figlio sinistro e figlio destro). Esempio Supponiamo che l input sia + il + cane + il + gatto + il + topo + il + gatto Dopo queste operazioni, viene costruito l insieme di occorrenze I occ così definito: L albero binario di ricerca ottenuto è: I occ = { il, 4, cane, 1, gatto, 2, topo, 1 } < il, 4 > <cane, 1> <topo, 1> <gatto, 2> Nota Se si cambia l ordine delle istruzioni di input, i nodi potrebbero essere disposti in modo diverso, tuttavia l insieme rappresentato è lo stesso I occ definito sopra. 3

Per rappresentare l albero delle occorrenze, in main() va definita una variabile root di tipo nodo* che rappresenta la radice dell albero. Questo significa che root vale NULL se l albero è vuoto, altrimenti root è l indirizzo del nodo radice dell albero. La rappresentazione in memoria dell albero della figura precedente è (per chiarezza le frecce corrispondenti ai campi up non sono segnate): root key up right word num 4 i l \0 left word num 1 c a n e \0 key key up right... up right... word num 1 t o p o \0 left left key up... word g a t t o \0 right num 2 left L output del comando p è: OCCORRENZE: cane 1 gatto 2 il 4 topo 1 L output del comando i è: OCCORRENZE: topo 1 il 4 gatto 2 cane 1 4

Struttura del codice Occorre completare il programma occ.c. Per gestire le operazioni sull albero si possono usare le funzioni per gli alberi binari di ricerca nel file tree.c. Tuttavia in C, a differenza di quanto avviene in Java (o più in generale nei linguaggi object oriented), per poter riutilizzare del codice occorre spesso intervenire per riscriverne delle parti. Le funzioni di tree.c riguardano alberi binari di ricerca di interi, infatti il campo next della struttura struct node (definita in tree.h) ha tipo int. Quindi vanno riscritte le parti in cui viene utilizzata l informazione del campo key di un nodo. Scrivere il codice delle operazioni seguendo l ordine qui indicato. addword(word) Occorre definire le seguenti funzioni. (1) La funzione occorrenza* newoccorrenza(char *w) crea l occorrenza w, 1 e restituisce un puntatore alla nuova occorrenza. L occorrenza deve contenere una nuova copia di w (usare la funzione newword()). (2) La funzione node* newnode(char* w) crea un nodo contenente l occorrenza w, 1 e restituisce un puntatore al nuovo nodo. Copiare la funzione newnode() di tree.c inizializzando correttamente il campo key del nuovo nodo (l occorrenza va creata con la funzione newoccorrenza()). (3) La funzione node *treeinsert(char *w, node *r) inserisce un nodo contenente l occorrenza w, 1 nell albero di radice r e restituisce l indirizzo del nuovo nodo. Copiare le funzione treeinsert() di tree.c facendo le dovute modifiche. In particolare, vanno modificate istruzioni n < q->key n < pq ->key che confrontano la chiave da inserire con la chiave di un nodo dell albero. Per il confronto, occorre definite una funzione strminore() per il confronto di parole. (4) La funzione node *findword(char *w, node *r) 5

cerca un nodo contenente l occorrenza di w nell albero r (ossia, il campo key del nodo è l indirizzo a una occorrenza contenente la parola w). Se il nodo esiste restituisce il suo indirizzo, altrimenti restituisce NULL. Copiare la funzione treefind() di tree.c riscrivendo opportunamente il codice per il confronto. In particolare, per controllare l uguaglianza definire una funzione struguale(). (5) La funzione void incrementaocc(node *p) incrementa di 1 il valore di num dell occorrenza in p. Si può ora scrivere il codice della funzione void addword(char *w, node **proot) che implementa l operazione addword(word), dove proot è un puntatore alla radice dell albero delle occorrenze (passaggio per indirizzo). Questo significa che, all interno della funzione, per denotare la radice dell albero si può usare l espressione *proot. Si noti che per implementare l operazione addword(word) occorre un parametro in più rispetto a quelli nominati nella sua definizione astratta. printocc(word) Occorre anzitutto scrivere il codice della funzione void printnode(node *p) che stampa l occorrenza nel nodo p. Scrivere quindi il codice della funzione void printocc(char *word, node* r) che implementa l operazione printocc(word), dove r è la radice dell albero delle occorrenze. Nell esempio precedente, le istruzioni? il? bue devono stampare il 4 bue 0 printtab() Per stampare la tabella in ordine alfabetico, occorre stampare l albero delle occorrenza facendo una visita inorder (se l albero non è vuoto, si stampa il sottoalbero sinistro, poi la radice, poi l albero destro). Scrivere il codice della funzione 6

void printinorder(node *r) che stampa l albero delle occorrenze r nel modo descritto. Scrivere quindi il codice della funzione void printtab(node *r) che implementa l operaziome printtab(), dove r è la radice dell albero delle occorrenze. printtabinv() Per stampare la tabella in ordine alfabetico inverso, occorre visitare i sottoalberi in ordine inverso rispetto al caso precedente. Procedere come nel punto precedente scrivendo il codice delle funzioni void printinorderinv(node *r) e void printtabinv(node *r) addfromfile(f ilen ame) Scrivere il codice della funzione void addfromfile(char* filename, node **proot) che implementa l operazione addfromfile(f ilen ame), dove proot è un puntatore alla radice dell albero delle occorrenze (passaggio per indirizzo). Occorre: - Aprire in lettura il file di nome filename con l istruzione fp = fopen(filename,"r"); dove la variabile fp deve avere tipo FILE* (file pointer). Se il file non esiste, fopen() restituisce NULL. - Leggere una parola per volta dal file fp chiamando fscanf(fp, "%s", word) dove word è un array di char (definire la dimensione in modo corretto!). La funzione fscanf() si comporta come scanf() (il primo parametro deve essere il file pointer associato al file da cui leggere), quindi salta automaticamente gli spazi fra le parole e resituisce 1 se è stata letta una stringa, EOF se il file è terminato. Per aggiungere la parola letta nell albero delle occorrenze usare addword(). - Al termine della lettura, il file fp va chiuso: fclose(fp); 7

delword(word) La funzione treedelete() di tree.c non richiede alcuna manipolazione. Occorre definire le seguenti funzioni. - La funzione void freenode(node *p) cancella la memoria occupata dal nodo p (va cancellato tutto lo spazio di memoria utilizzato per rappresentare il nodo). - La funzione void decrementaocc(node *p) decrementa di 1 il valore di num dell occorrenza in p. Si può ora scrivere il codice di void delword(char *word, node **proot) che implementa l operazione delword(word), dove proot è un puntatore alla radice dell albero delle occorrenze (passaggio per indirizzo). Ulteriori operazioni 1. Aggiungere una operazione per cancellare tutta la tabella. 2. Aggiungere una operazione per stampare la tabella in ordine di numero di occorrenze. Nell esempio precedente deve essere stampato: OCCORRENZE: il 4 gatto 2 cane 1 topo 1 In questo caso la struttura dati non permette di usare una strategia efficiente (non sempre esiste una struttura dati che risulti efficiente per tutte le operazioni richieste!). Occorre copiare le occorrenze in un array e ordinarlo. 3. Assumere che le parole abbiano lunghezza arbitraria (occorre scrivere una opportuna funzione strread() che legge una parola la cui lunghezza non è nota a priori e restituisce l array contenente la parola letta). 8

1 Esercizio 2: alberi generati casualmente Lo scopo è quello di scrivere un programma che genera casualmente un albero binario di ricerca contenente 2 k nodi, dove k è un valore inserito dall utente, in modo da valutare sperimentalmente quanto l altezza dell albero ottenuto si discosti dal valore minimo k e dal valore massimo 2 k 1. Per semplicità, consideriamo alberi di interi. 1. Scrivere in un file trand.c il codice della funzione node *treerand(int k) che genera un albero binario di ricerca contenente k nodi distinti generati casualmente e restituisce la radice dell albero ottenuto. Usare le funzioni in tree.c (in trand.c occorre includere tree.h). Suggerimento. Anzitutto occorre creare l albero vuoto. Quindi, si genera con rand() un intero casuale e, se non è già contenuto nell albero (verificarlo con treefind()), lo si inserisce (con treeinsert()). L operazione va ripetuta fino a quando sono stati inseriti k interi, quindi si restituisce la radice dell albero. In realtà la funzione non è molto efficiente nel caso k assuma valori molto alti in quanto il generatore potrebbe aver difficoltà a generare numeri nuovi (non ancora inseriti nell albero). 2. Per verificare il comportamento di treerand(), scrivere in trand.c una funzione main() che legge un intero k da standard input, genera un albero casuale di k elementi e lo stampa. Il programma termina quando viene inserito 0. Per compilarlo: gcc trand.c tree.c Ricordarsi che il generatore di numeri casuali va inizializzato ponendo all inizio di main() l istruzione srand(time(null)); // occorre includere <time.h> 3. Scrivere un programma che legge da standard input un intero k, genera un albero casuale di 2 k nodi e stampa il valore dell altezza dell albero ottenuto. Il programma termina quando viene inserito 0. Risultati ottenuti con k = 10 (1024 nodi): Altezza: 18 Altezza: 22 Altezza: 20 L altezza è vicina al valore minimo 10 (come ci si aspetta dal teorema visto a lezione). 4. Scrivere un programma che legge da standard input due interi k e h, quindi genera degli alberi casuali di 2 k nodi terminando quando viene generato un albero di altezza maggiore o uguale a h (si assume k h 2 k 1). Al termine dell esecuzione, il programma stampa il numero di alberi generati. 9

Esercizio 3: costruzione di un albero binario Lo scopo dell esercizio è quello di scrivere delle funzioni per costruire un alberi binario di interi a partire dalla sua definizione. Per l input usiamo la seguente convenzione. - L albero vuoto è denotato dal carattere b. - L albero n, T 1, T 2 (dove n è un intero positivo) è denotato da < n L R > dove L e R denotano rispettivamente T 1 e T 2. La descrizione di un albero è letta da standard input e deve terminare con end-of file. I vari simboli (token) della descrizione possono essere separati da uno o più caratteri di spaziatura (spazio, a capo, tabulazione, ecc.). L albero 50 13 75 38 60 55 85 può essere descritto da < 50 < 13 b <38 b b> > < 75 < 60 < 55 b b> <85 b b> > b > > oppure <50 <13 b <38bb>> < 75 <60 <55bb><85bb>> b> > La descrizione di un albero viene codificata nel programma come una successione di interi (che sarà memorizzata in un array). Per codificare i caratteri <, > e b in modo che non vengano confusi con i valori di un nodo usare le seguenti definizioni: #define BEGIN_TREE -1 // inizio albero #define END_TREE -2 // fine albero #define EMPTY_TREE -3 // albero vuoto 10

Ad esempio, < 10 b <20bb> > è memorizzato come: 1 10 3 1 20 3 3 2 2 a. Scrivere il codice della funzione int nextsymbol() che legge il prossimo simbolo da standard input e restituisce il codice corrispondente al simbolo letto. Se viene letto end-of-file, restituisce STOP definito come #define STOP -4 Si noti che in questo caso è pericoloso restituire EOF perché tale valore potrebbe coincidere con uno dei valori usati nelle definizioni precedenti. Suggerimento. Conviene anzitutto cercare di leggere da standard input un intero con la chiamata scanf("%d", &n) Se questa ha successo si restituisce l intero letto. In caso contrario, occorre leggere il prossimo carattere da standard input e restituire il valore opportuno (se il carattere letto è b va restituito EMPTY TREE, se è < va restituito BEGIN TREE, ecc.). b. Scrivere il codice della funzione int endtree(int tree[], int begin) che, dato un array tree[] di interi contenente la codifica di un albero T e un indice begin di tree[] tale che in tree[begin] inizia la codifica un sottoalbero T di T, restituisce l indice dell array tree[] in cui termina la codifica di T. Suggerimento. Per ipotesi, in tree[begin] si trova il valore EMPTY TREE oppure BEGIN TREE. Nel primo caso occorre restituire begin. Nel secondo caso bisogna cercare il simbolo END TREE che chiude il BEGIN TREE in tree[begin]. È sufficiente scorrere l array fino a quando il numero di END TREE è uguale a quello di BEGIN TREE. c. Scrivere il codice della funzione node *buildtree(int tree[], int begin) che, dato un array tree[] e un indice begin di tree[] tale che in tree[begin] inizia la codifica di un albero T, costruisce T e restituisce l indirizzo della radice di T. Suggerimento. Se l albero non è vuoto, fare due chiamate ricorsive per costruire i sottoalberi (per sapere dove inizia il codice del sottoalbero destro usare la funzione endtree() del punto precedente). Se i punti precedenti sono stati svolti correttamente, il codice seguente legge da standard input la descrizione di un albero, costruisce l albero e stampa i nodo in preorder (prima stampa la radice, poi ricorsivamente il sottoalbero sinistro e il sottoalbero destro). Si assume che la codifica dell albero contenga al massimo MAX simboli. 11

# define MAX 10000 // massimo numero simboli di una codifica... /* Stampa in preorder l albero di radice r */ void print(node* r){ if(r! = NULL){ printf("%d\n", r->key); // stampa radice print(r->left); // stampa sottoalbero sinistro print(r->right); // stampa sottoalbero destro } } int main(){ int k, s; node *root; // radice dell albero int treedescriptor[max]; // memorizza codifica dell albero for(k=0 ; (s=nextsymbol())!= STOP ; k++) // lettura input treedescriptor[k] = s; root = buildtree(treedescriptor,0); // costruisce albero print(root); // stampa albero return 0; } Supponiamo ora di voler costruire alberi di interi in cui i nodi possono assumere un qualunque valore intero. Il programma dell esercizio precedente non funziona in quanto, ad esempio, un nodo di valore 1 verrebbe confuso con BEGIN TREE. Per risolvere il problema senza dover riscrivere tutto il codice, per rappresentare un nodo di valore n usiamo la seguente codifica Code(n) = { n se n 0 n 4 se n < 0 In questo modo, l albero < -1 b <-2 bb> > è codificato come 1 5 3 1 6 3 3 2 2 e ogni intero della codifica è univocamente interpretabile. c. Scrivere il codice della funzione int code (int n) che, dato un intero n, restituisce la sua codifica Code(n). Scrivere anche la funzione inversa decode() che, dato un valore k tale che k 0 oppure k 5, restituisce l intero n codificato da k (ossia, tale che Code(n) = k). d. Modificare la funzione nextsymbol() dell esercizio precedente facendo in modo che, nel caso venga letto un intero n, restituisca Code(n). 12

Per poter utilizzare il programma di prima, occorre ancora modificare la funzione per la stampa, in quanto il valore di un nodo, prima di essere stampato, va decodificato. void print(node * r){ if(r!= NULL){ printf("%d\n", decode(r->key)); // stampa nodo r print(r->left); print(r->right); } } 13