La lezione è centrata su un esercizio per impratichirsi sui seguenti argomenti:

Documenti analoghi
Laboratorio di Algoritmi

Laboratorio di algoritmi e strutture dati

Implementazione dell albero binario in linguaggio C++

PROGRAMMAZIONE II canale A-D luglio 2008 TRACCIA DI SOLUZIONE

La lezione è centrata su un esercizio per impratichirsi sui seguenti argomenti:

d. Cancellazione del valore 5 e. Inserimento del valore 1

Alberi. CORDA Informatica. A. Ferrari. Testi da. Marco Bernardo Edoardo Bontà. Dispense del Corso di. Algoritmi e Strutture Dati

In questa lezione Strutture dati elementari: Pila Coda Loro uso nella costruzione di algoritmi.

Laboratorio di algoritmi e strutture dati

Espressioni aritmetiche

Alberi binari e alberi binari di ricerca

Alberi binari e alberi binari di ricerca

Algoritmi e strutture dati

La lezione è centrata su un esercizio per impratichirsi sui seguenti argomenti:

Alberi binari e alberi binari di ricerca

Alberi. Strutture dati: Alberi. Alberi: Alcuni concetti. Alberi: definizione ricorsiva. Alberi: Una prima realizzazione. Alberi: prima Realizzazione

Alberi n-ari: specifiche sintattiche e semantiche. Realizzazioni. Visita di alberi n-ari.

L albero e un tipo astratto di dati usato per rappresentare relazioni gerarchiche.

Alberi ed Alberi Binari

Alberi. Alberi: definizioni. Alberi Binari. Esercizi su alberi binari: metodi ricorsivi. Struttura dati per alberi generici. ASD-L - Luca Tesei

Alberi e alberi binari I Un albero è un caso particolare di grafo

Note per la Lezione 4 Ugo Vaccaro

Alberi e alberi binari I Un albero è un caso particolare di grafo

Alberi binari. Alberi Albero binario Heap tree

liste ogni nodo ha un successore, tranne l ultimo della lista che ne ha zero; alberi binari ogni nodo ha zero, uno oppure due figli

Strutture dati Alberi binari

Corso di Programmazione

Alberi binari di ricerca

Esercizi su alberi binari

Corso di Programmazione

Laboratorio di Python

Lezione 8 Struct e qsort

Corso di Programmazione

Heap e code di priorità

Spesso sono definite anche le seguenti operazioni:

Problemi di ordinamento

Ripasso di programmazione ricorsiva

Esercizi su strutture dati

Il vettore e la lista concatenate (o catena) sono due esempi di strutture interne.

Le classi in java. Un semplice programma java, formato da una sola classe, assume la seguente struttura:

LE STRUTTURE DATI DINAMICHE: GLI ALBERI. Cosimo Laneve

Alberi binari di ricerca

Divide et impera su alberi

In questa lezione. Alberi binari: [CLRS09] cap. 12 per la visita inorder. visite e esercizi su alberi binari. Prof. E. Fachini - Intr. Alg.

Algoritmi e Strutture Dati

Albero in cui ogni nodo ha al più due figli. I figli di un nodo costituiscono una coppia ordinata

Algoritmi e Strutture Dati

Algoritmi e Strutture Dati

Informatica Generale Homework di Recupero 2016

Code a priorità Una coda a priorità è una struttura dati astratta che permette di rappresentare un insieme di elementi su cui è definita una

Lezione 7 Alberi binari: visite e alberi di ricerca

Moltiplicazione veloce di interi

Valutazione di espressioni

Lezione 6 Struct e qsort

Laboratorio di Informatica. Lezione 8: Liste e alberi

Albero binario. Alberi binari (introduzione) Terminologia. Alberi di ricerca binaria (BST)

Algoritmi Greedy. Tecniche Algoritmiche: tecnica greedy (o golosa) Un esempio

Algoritmi e Strutture Dati. HeapSort

Esempi. Albero di interi. Struttura che non è un albero!

Esercitazioni di Fondamenti di Informatica - Lez. 8 27/11/2018

Esercitazione 8. Corso di Tecniche di programmazione. Laurea in Ingegneria Informatica

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Algoritmi e strutture dati

TRIE (albero digitale di ricerca)

Esercizio 1. E vero che in un AVL il minimo si trova in una foglia o nel penultimo livello? FB = -1. livello 0 FB = -1. livello 1 FB = -1.

Linguaggi di programmazione - Principi e paradigmi 2/ed Maurizio Gabbrielli, Simone Martini Copyright The McGraw-Hill Companies srl

Il TDA BinaryTree. Albero Binario. Albero sintattico. Applicazioni. Albero delle decisioni binario. Albero binario di ricerca

Struttura dati Dizionario

Traduzione guidata dalla sintassi. Attributi e Definizioni guidate dalla sintassi

ESPRESSIONI. Ivan Lanese

Intro. Traduzione guidata dalla sintassi. Attributi. Due notazioni a diversi livelli. Due notazioni a diversi livelli. Il flusso concettuale

B.1 I grafi: notazione e nomenclatura

Informatica 3. LEZIONE 16: Heap - Codifica di Huffmann. Modulo 1: Heap e code di priorità Modulo 2: Esempio applicativo: codifica di Huffmann

Informatica 3. Informatica 3. LEZIONE 23: Indicizzazione. Lezione 23 - Modulo 1. Indicizzazione. Introduzione. Indicizzazione:

Organigramma Gerarchia. (Tree) Nessuna persona può avere più di un superiore Ogni persona può essere superiore di altre

Esercizi su ABR. Prof. E. Fachini - Intr. Alg.!1

Soluzioni della settima esercitazione di Algoritmi 1

Algoritmi e strutture dati

COGNOME E NOME (IN STAMPATELLO) MATRICOLA

Introduzione agli algoritmi Prova di esame del 19/9/2016 Prof.sse E. Fachini - R. Petreschi. Parte prima

Algoritmi e Strutture Dati. HeapSort

Alberi Binari di Ricerca

Lezione 9 Alberi binari: visite ed esercizi

Alberi Binari di Ricerca

Implementazione di DFA in C

Il tipo astratto coda con priorità: specifiche sintattiche e semantiche. Realizzazioni.

Corso di Programmazione

Databases. Architettura di un DBMS: Struttura ad indice per i files, B + -Trees

Progettazione di Algoritmi: Approccio Top-Down e Bottom Up

Algoritmi e Strutture Dati & Laboratorio di Algoritmi e Programmazione

7. Settima esercitazione autoguidata: alberi binari

Gestione degli impegni Requisiti generali Si fissi come ipotesi che la sequenza di impegni sia ordinata rispetto al tempo,, e che ogni lavoratore abbi

C: panoramica. Violetta Lonati

Esercitazione 6. Alberi binari di ricerca

Alberi. Gli alberi sono una generalizzazione delle liste che consente di modellare delle strutture gerarchiche come questa: Largo. Fosco.

Transcript:

1 Lezione 15 La lezione è centrata su un esercizio per impratichirsi sui seguenti argomenti: alberi binari rappresentati con puntatori alberi binari rappresentati con cursori Chi notasse eventuali incoerenze o errori, oppure avesse dubbi sul contenuto di queste pagine e dei codici, è pregato di segnalarmeli per contribuire a migliorare la qualità dei materiali del corso. Esercizio Si vuole scrivere un programma calcola.c che elabora espressioni aritmetiche. Il programma acquisisce le espressioni da un file di testo, che contiene un espressione in ogni riga, con una lunghezza non superiore a 256 caratteri. Le espressioni sono riportate in notazione prefissa (o polacca), cioè assumono il seguente formato 1 : espressione semplice: un numero reale; espressione composta: operatore(operando1,operando2 ), dove 1. operatore è il nome di un operatore aritmetico, cioè una stringa qualsiasi fra somma, diff, prod, div 2. operando1 e operando2 sono due espressioni aritmetiche, semplici o composte. Il file input01.txt riporta il seguente esempio di espressione composta: il cui valore è prod(somma(5,3),div(10,diff(4,2)) (5 + 3) [10/ (4 2)] = 8 (10/2) = 8 5 = 40 Il programma deve calcolare e restituire il valore dell espressione, convertirla nella classica notazione infissa (quella con i simboli +,, e / inseriti fra gli operandi) e analizzarne la struttura, determinandone la profondità e se sia bilanciata o no. Traccia Prima di eseguire l esercizio, si possono scorrere interamente i lucidi per acquisire il concetto di albero binario e le basi delle implementazioni di alberi binari con puntatori. Quindi si può ragionare un po sul modello del problema. Un espressione aritmetica composta ha una struttura gerarchica e ricorsiva che si presta benissimo ad essere rappresentata con un albero. Le operazioni aritmetiche qui considerate sono tutte binarie, per cui l albero può essere binario, anche senza introdurre la convenzione dei puntatori al primo figlio e al fratello successivo. Inoltre, la notazione prefissa consente di costruire l albero identificando l operatore, che corrisponde alla radice di un albero e gli operandi, che corrispondono ai due sottoalberi sinistro (il primo operando) e destro (il secondo). Le parentesi e le virgole servono solo al lettore umano: il programma le ignora. Il programma dovrà costruire l albero binario che rappresenta ogni espressione sfruttando il fatto che la notazione prefissa presenta alla lettura prima il nodo padre e poi i suoi due figli, facilitando 1 In realtà, la notazione polacca non usa parentesi, dato che sono ridondanti. In questo esercizio introduciamo le parentesi per maggiore leggibilità. 1

la costruzione dell albero stesso. Merita osservare che, quindi, la rappresentazione di partenza dell espressione corrisponde esattamente a una visita in pre-ordine dell albero corrispondente. Il file di partenza, start.c, contiene: 1. le direttive per simulare il tipo boolean; 2. la costante simbolica ROW LENGTH per gestire le righe in lettura e scrittura. 3. la variabile file espressioni che conserva il nome del file delle espressioni; 4. la funzione InterpretaLineaComando che recupera il nome del file dalla linea di comando e lo copia nella corrispondente variabile. Si avvia la realizzazione top-down dell algoritmo scrivendo opportune funzioni e aggiungendo al codice la loro chiamata nel main, la dichiarazione fra i prototipi prima del main, la definizione vuota dopo il main. Della funzione principale ElaboraEspressioni è già presente uno scheletro, con un ciclo che scorre il file, caricando ciascuna riga in una stringa in modo da poterla elaborare. A questo punto bisogna: 1. caricare l espressione corrente dalla stringa in un albero; 2. calcolare e stampare il valore dell espressione rappresentata dall albero; 3. stampare in notazione infissa l espressione rappresentata dall albero; 4. calcolare la profondità dell albero; 5. distruggere l albero. Per poter scrivere queste funzioni, occorre avere a disposizione una libreria per la gestione di alberi. È disponibile il file header albero.h, che fornisce un implementazione a puntatori di albero binario con: l operatore dichiarato come un carattere l operando dichiarato come un numero reale l albero dichiarato come un puntatore a un nodo la posizione dichiarato come un puntatore a un nodo il nodo dichiarato come una struttura contenente un operatore e un operando (questo è uno spreco, perché uno solo dei due campi sarà utilizzato, ma semplifica le cose) e tre posizioni: quella del padre e quelle delle radici del sottoalbero sinistro e destro. Sono anche definite delle costanti simboliche per rappresentare operatori fittizii (NO OP), operandi fittizii (NO VAL), alberi vuoti (NO TREE) e posizioni fittizie (NO NODE). Il file albero.c fornisce solo i corpi vuoti delle funzioni; quelle che devono restituire un valore ne restituiscono uno fittizio convenzionale. Per cominciare, li riempiremo realizzando un implementazione a puntatori della libreria 2. È utile osservare che la 2 Nota: la scelta degli operatori per una struttura dati astratta è in buona parte convenzionale: testi diversi propongono operatori diversi, anche se in genere largamente sovrapposti. La scelta specifica dipende dalle operazioni che si vogliono eseguire con la libreria. Quella qui considerata deriva dal testo di Alan Bertossi, salvo che la funzione costruiscealbero produce un albero a partire da due sottoalberi e dall informazione associata alla radice, anziché solo dai due sottoalberi, il che porterebbe a generare un albero con una radice priva di informazione, da aggiungere poi con la funzione scrivenodo. Mi è parso più ragionevole seguire la strada indicata nei lucidi. 2

cancellazione di un sottoalbero (come pure la distruzione di un intero albero, che ne è solo un caso particolare), richiede la visita del sottoalbero in post-ordine, dato che prima bisogna deallocare i sottoalberi figli e solo dopo il nodo radice; altrimenti, non sarebbe più possibile raggiungerli. Il risultato di questa fase della lezione sono i file calcola2.c e albero-finale.c 3. Completata la libreria per gestire gli alberi binari, torniamo all approccio topdown e realizziamo la conversione dell espressione in albero con un opportuna funzione ConverteEspressione. Ci sono due casi: se l espressione è semplice, cioè ridotta a un numero reale, basta costruire un albero con la sola radice e due sottoalberi vuoti, contenente come operando il numero reale e un operatore fittizio; si può verificare che l espressione è semplice con una semplice chiamata a sscanf; se l espressione è composta, occorre dividerla nelle sue tre stringhe componenti: 1. operatore: la stringa iniziale sino alla prima parentesi aperta, esclusa; 2. primo operando: la stringa compresa fra la prima parentesi aperta, esclusa, e la virgola che separa i due operandi (che non è in genere la prima virgola!) esclusa; 3. secondo operando: la stringa finale che parte dalla virgola separatrice, esclusa, fino alla parentesi conclusiva, esclusa. Se ne occuperà un opportuna funzione ScomponeEspressione, che realizzeremo in seguito. Una volta scomposta l espressione, l operatore va tradotto da una stringa (somma, diff, prod o div) nel carattere corrispondente ( +, -, * o / ), con un opportuna funzione IdentificaOperatore. I due operandi, invece, vanno ulteriormente analizzati per convertirli in alberi. Questo si può fare agevolmente in maniera ricorsiva, dato che sono espressioni e stiamo proprio definendo la funzione ConverteEspressione che converte un espressione in un albero. Al termine, abbiamo un operatore e due alberi che possiamo fondere in un albero con la funzione di libreria costruiscealbero. Il risultato di questa fase della lezione è il file calcola3.c. A questo punto, procediamo a realizzare la funzione IdentificaOperatore, che è una banale serie di confronti in cascata (if... else if... else), condotti con la funzione strcmp, e la funzione ScomponeEspressione, che scorre la stringa data copiandone via via i caratteri in tre stringhe ausiliarie. È possibile fare questo in modo abbastanza elegante, riconoscendo che i tre compiti sono molto simili fra loro: la fine dell operatore è indicata dalla prima parentesi aperta, la fine del primo operando da una virgola e la fine del secondo operando da una parentesi chiusa. Il problema è che la virgola e la parentesi chiusa che indicano la fine dei due operandi non sono in generale le prime: bisogna evitare di fermarsi a una virgola o una parentesi annidate in altre parentesi di livello inferiore. Per esempio, in prod(somma(5,3),div(10,diff(4,2)) l operatore è prod, mentre il primo operando non è somma(5, bensì somma(5,3). Non è difficile risolvere questa difficoltà: si tratta di trovare la prima occorrenza di un carattere separatore, che non sia annidata all interno di una coppia di parentesi. Dunque, si tratta di scorrere 3 In aula quest anno ho seguito un ordine diverso, privilegiando il progetto top-down, cioè implementando la libreria solo dopo la stesura della procedura ConverteEspressione: sono puramente scelte di gusto. 3

la stringa tenendo conto del numero di parentesi aperte che non siano ancora state chiuse: il separatore va accettato solo se questo numero è nullo. La procedura TrovaCarattereNonAnnidato fornisce la posizione del separatore, che può essere poi usata per copiare pezzi dell espressione originale nelle tre stringhe componenti. Il risultato di questa fase della lezione è il file calcola4.c. A questo punto, si può procedere a realizzare la funzione CalcolaValore, che valuta l espressione rappresentata dall albero. Ancora una volta, il modo più semplice è procedere ricorsivamente. Si ha: un caso base corrispondente all albero vuoto, per il quale si restituisce il valore fittizio NO VAL; un secondo caso base nel quale la radice contiene un operando e nessun operatore (cioè l operatore fittizio NO OP); un caso ricorsivo nel quale occorre prima valutare le due espressioni figlie e poi applicare l operatore ai loro valori. Si noti che tutto questo equivale a una visita in post-ordine dell albero stesso, dato che abbiamo prima valutato i nodi figli e poi ricavato da loro il valore del padre. Il risultato di questa fase della lezione è il file calcola5.c. Passiamo quindi a realizzare la funzione StampaEspressione che stampa l espressione rappresentata dall albero nella classica notazione infissa, con l operatore intermedio fra i due operandi. Per evitare ambiguità, dobbiamo aggiungere le parentesi tonde (che nella notazione prefissa di partenza erano presenti, ma in realtà non necessarie: qui invece lo sono. Naturalmente, l implementazione sarà ancora ricorsiva, con due casi base (albero vuoto ed espressione semplice) e un caso ricorsivo, nel quale l elaborazione del figlio sinistro precede quella dell operatore in radice, che precede quella del figlio destro. Si tratta quindi di una visita in in-ordine. Il risultato di questa fase della lezione è il file calcola6.c. Il calcolo della profondità è anch esso ricorsivo: nel caso base, la profondità è per convenzione nulla; nel caso ricorsivo, la profondità è la maggiore fra le profondità dei due sottoalberi, incrementata di 1 per tener conto del livello aggiuntivo introdotto dalla radice. Si tratta ancora di una visita, ma in effetti l elaborazione del nodo radice è talmente banale (sommare 1) che non ha molto senso cercare di classificare il tipo di visita. Comunque, la complessità è Θ (n) come in tutti i casi precedenti. Il risultato di questa fase della lezione è il file calcola7.c. A questo punto, si può procedere a creare una nuova libreria di gestione, basata sull implementazione con cursori. La differenze fondamentali sono: 4 l albero è ospitato in un vettore allocato una volta per tutte dalla funzione creaalbero; la dimensione non varia durante l esecuzione (se è insufficiente, si esce con un messaggio di errore) e il suo valore TREE SIZE+1 tiene conto degli elementi massimi ospitabili e della testa di una lista libera, che ospita gli elementi del vettore non impiegati nell albero corrente i puntatori sono sostituiti da indici numerici; per accedere ai nodi, bisogna quindi conoscere sia la posizione sia l albero: il nodo in posizione p dell albero T è banalmente T[p] 4 Quanto segue è stato frettolosamente riadattato dalla lezione sulle liste: si prega di segnalare eventuali errori o incongruenze. 4

la creazione dell albero richiede l allocazione del vettore, e la costruzione di una lista libera contenente tutti i nodi inserimenti e cancellazioni corrispondono a spostamenti fra albero e lista libera Pur essendo molto diversa dalla precedente, la nuova implementazione non richiede nessuna modifica al file calcola.c: potenza delle strutture dati astratte. 5 5 Per lo meno, non dovrebbe richiedere alcuna modifica, ma non l ho ancora implementata per verificare se è così. 5