====== Esercizi liste ====== ===== Esercizio 1: Sequenze di double (discusso a lezione) ===== Consideriamo il problema di leggere da standard input una sequenza di valori reali terminata da 0.0 creando la lista corrispondente di tipo typedef struct lista_d { double val; struct lista_d * next; } lista_d_t ; * sviluppare un programma C che legge un singolo elemento dallo standard input usando una funzione double leggi_nuovo_valore(void); e utilizza le funzioni lista_d_t * inserisci_testa ( lista_d_t * l, double v); lista_d_t * inserisci_coda ( lista_d_t * l, double v); lista_d_t * inserisci_ord ( lista_d_t * l, double v); per inserire nella lista. La scelta del tipo di inserizione viene richiesta all'utente una volta per tutte prima di inserire i valoro. Inoltre, si utilizza una funzione ''stampa_lista()'' per stampare la lista inserita prima di terminare. ===== Esercizio 2: Altre funzioni su liste di double ===== Implementare le seguenti funzioni in modo iterativo e ricorsivo: /** calcola e restituisce il massimo della lista l */ double max ( lista_d_t * l); /** calcola e restituisce la somma di tutti gli elementi della lista l*/ double somma ( lista_d_t * l); /** libera la memoria occupata dalla lista */ void free_list( lista_d_t * l); /** cancella, se presente, l'elemento che contiene la prima occorrenza del valore v dalla lista l (liberando la memoria) e restituisce la nuova lista */ lista_d_t * cancella_uno ( lista_d_t * l, double v); /** cancella tutti gli elementi di valore del valore v dalla lista l (liberando la memoria) e restituisce la nuova lista */ lista_d_t * cancella_tutti ( lista_d_t * l, double v); e sviluppare un main() che ne testa il funzionamento. ===== Esercizio 3: Verifica di proprietà su liste (ricorsivamente) ===== Definire una funzione ricorsiva che verifica che per ogni elemento di una lista valga la proprietà \[ l(i + 1) = 2*l(i) \] dove $l(i)$ indica l'i-esimo elemento della lista a partire dalla testa. La funzione restituisce ''true'' se la proprietà è verificata e ''false'' altrimenti. I valori booleani sono definiti con un opportuno tipo enumerato. ===== Esercizio 4: Liste con doppio puntatore ===== Realizzare le funzioni dell'esercizio 1 utilizzando liste con puntatore al precedente e al successivo typedef struct elem_d { double val; struct elem_d * prec; struct elem_d * next; } elem_d_t ; in questo caso la lista complessiva puo' essere definita ad esempio come una struttura con due puntatori, uno alla testa ed uno alla coda typedef struct lista_d { struct elem_d * head; struct elem_d * tail; } lista_d_t ; come si modificano gli algoritmi sviluppati precedentemente ? ===== Esercizio 5: (avanzato) Array sparsi (//a la Python//) implementati come liste ===== Vogliamo realizzare degli array di double di grandi dimensioni che contengono solo una piccola percentuale di elementi diversi da 0 come liste concatenate definite come typedef struct sparse_d { double val; unsigned index; struct elem_d * next; } sparse_d_t ; nella lista sono presenti solo gli elementi diversi da zero e per ogni elemento e' indicato l'indice a cui corrisponde. La Lista e' mantenuta ordinata rispetto al campo indice. Realizzare le funzioni ''put'' e ''get'' che permettono di leggere il valore dell'elemento di un certo indice ''i'' (''get'') e di modificarne il valore (''put''), un possibile prototipo per queste funzioni e': double put (sparse_d_T * a, unsigned indice); sparse_d_t * put (sparse_d_T * a, double x, unsigned indice);