22 5. Quinta esercitazione autoguidata: liste semplici 5.1. Liste rappresentate mediante strutture e puntatori (LISTE1.C, LISTE2.C) Scrivere un programma che - costruisce una lista di k interi (con k letto da input) - la stampa - chiede un nuovo intero e lo inserisce in testa alla lista - stampa di nuovo la lista Per la costruzione della lista usare la funzione di costruzione costruiscilista() che, ricevendo un parametro intero n, costruisca una lista di n elementi mediante inserimenti in testa. Per la stampa degli elementi della lista si usa la funzione stampalista()che, ricevendo il puntatore al primo elemento di una lista, stampa la lista. Ecco i prototipi delle funzioni richieste (+1): void instestalista(tipolista * plis, TipoElemLista elem); /* inserisece elem nella lista; plis e' l'indirizzo della variabile puntatore che punta all'inizio della lista */ TipoLista costruiscilista (int n); /* costruisce una lista di n elementi e ne restituisce il puntatore all'inizio (usa instestalista() */ void stampalista(tipolista lis); /* stampa tutti i dati contenuti nella lista */ Suggerimento2: questi potrebbero essere i tipi utilizzati: NB tutte queste strutture dati devono essere definite "top level", in modo che siano visibili a tutte le funzioni definite poi (la main() e le altre funzioni). Infatti se definissimo queste strutture dati direttamente nella main(), esse non sarebbeo poi utilizzabili da stampalista o costruiscilista() o quant'altro typedef int TipoElemLista; /* si tratta di liste di interi quindi il tipo delle informazioni nei nodi della lista e' int */ struct StructLista { /* il tipo per i singoli nodi della lista */ TipoElemLista info; struct StructLista *next; ; typedef struct StructLista * TipoLista; /* il tipo delle variabili che rappresentano liste all'interno del programma */
23 typedef TipoLista PuntNodoLista; /* un tipo ausiliario: sara' il tipo delle variabili puntatore a nodi di liste, ad esempio usate per le scansioni di lista */ 5.2. Funzioni adatte al tipo delle informazioni contenute in lista (LISTE2.C) Per rendere il programma meno dipendente dal tipo di elementi contenuti nei nodi della lista, non sarebbe male corredare i programma di due funzioni stampaelemlista() e leggielemlista() adatte per leggere e stampare singoli elementi della lista. ecco i prototipi delle funzioni richieste: void stampaelemlista(tipoelemlista v); /* stampa l'oggetto v di tipo TipoElemLista */ void leggielemlista(tipoelemlista *pelem); /* legge un oggetto di tipo TipoElemLista; e lo memorizza nella locazione puntata da pelem */ In questo programma l'uso di tali funzioni puo' sembrare ridondante: si tratta di semplici usi di printf e scanf su variabili intere; ma in programmi in cui l'informazione associata ai nodi della lista e' di tipo piu' complesso queste funzioni permetteranno di una migliore strutturazione del programma. 5.3. Liste di caratteri (LISTE3.C) Analogamente al caso precedente, scrivere un programma che - costruisce una lista di k CARATTERI (con k letto da input) - la stampa Si puo sfruttare il programma precedente, avendo l accortezza di adattare le funzioni di lettura e stampa di elementi da inserire in lista (e solo quelle...) 5.4. Liste di caratteri senza sapere prima quanti sono (LISTE4.C) Scrivere un programma che legga una sequenza di caratteri inserita da tastiera e terminata da un \n (invio) e costruisca e stampi la lista corrispondente. Diversamente dall esercizio precedente, qui non sappiamo prima della costruzione quanti saranno i dati da inserire in lista. Dobbiamo realizzare un ciclo di inserimenti in testa che termina quando il carattere letto da input e \n. Suggerimento 2: uno schema possibile, facendo a meno della funzione costruiscilista() e realizzando il ciclo di lettura dati ed inserimento direttamente nella funzione main():
- lettura primo dato - mentre il dato letto non e \n o inserimento del dato lettpo precedentemente (con instestalista()) o lettura del prossimo dato (con leggielemlista()) 24 Suggerimento 3: ecco uno stralcio del programma LISTE4.C: mostriamo solo il cicloche esegue l inserimento dell ultimo dato letto (diverso da \n e la lettura del prossimo. Le variabili citate sono definite nel main, con significato comprensibile dai rispettivi identificatori) while (datiterminati==0) { instestalista(&list, el); leggielemlista(&el); /* lettura elemento successivo */ datiterminati=(el=='\n'); /* assegnazione variabile per la condizione di ripetizione */ 5.5. Inserimento in coda (LISTE5.C) Scrivere un programma che legga una sequenza di caratteri inserita da tastiera e terminata da un \n (invio) e costruisca e stampi la lista corrispondente. Ma stavolta e richiesto che l ordine degli elementi in lista corrisponda esattamente a quello di inserimento: il primo elemento in lista deve essere quello inserito in input per primo e cosi via... Si consiglia di costruire il ciclo di inserimenti in coda direttamente nella main(). ecco i prototipi delle funzioni definite nel file LISTE5.C: int ugualielem(tipoelemlista, TipoElemLista); /* aggiunta, per confrontare elemnti della lista */ void stampalista(tipolista lis); void stampaelemlista(tipoelemlista v); void leggielemlista(tipoelemlista *pelem); 5.6. Inserimento in coda (LISTE6.C) Si richiede di eseguire l esercizio precedente utilizzando la tecnica di inserimento in coda con l uso del record generatore: - pgen sia il puntatore al record generatore (una struttura fittizia che rimane vuota di informazione ma ha come nodo successore quello che alla fine sara il primo nodeo della lista); - inizialmente ultimo=pgen; - poi si esegue un ciclo di inserimenti in coda, usando e aggiornando ultimo: o allocazione di un nodo puntato da ultimo->next o aggiornamento di ultimo in modo che punti al nuovo nodo allocato in coda o inserimento del dato in ultimo->info o lettura del prossimo dato (quello da inserire alla prossima iterazione, o quello che fara terminare il ciclo di inserimenti).
5.7. Inserimento in coda 2 (LISTE7.C) 25 Eseguire l esercizio precedente, ma definendoo ed utilizzando una funzione apposita per eseguire gli inserimenti in coda, il cui prototipo sia il seguente: int inscodalista(tipolista * plis, TipoElemLista elem); questa funzione inserisce elem in coda alla lista; plis e' l' indirizzo della variabile puntatore che punta all' inizio della lista. (Quindi si usa questa funzione per eseguire un inserimento in lista). Ed ecco come viene usata: leggielemlista(&el); /* prima lettura */ while (!ugualielem(el, '\n')) { /* inserimento in coda del dato letto precedentemente */ ins=inscodalista(&list, el); if (ins==0) { /* se c sono stati problemi usciamo dal ciclo */ printf("\nproblemi in inserimento\n"); break; leggielemlista(&el); /* lettura elemento successivo */ 5.8. Inserimento in coda 3: i file (LISTE8.C) Scrivere un programma che - costruisca una lista con i dati (interi) letti da un file di testo - stampi la lista il nome del file di testo deve essere ricevuto da input ecco i prototipi delle funzioni usate nel programma LISTE8.C void stampalista(tipolista lis); void stampaelemlista(tipoelemlista v); int inscodalista(tipolista * plis, TipoElemLista elem); /* aggiunta per leggere da file gli elementi della lista */ void leggielemlistadafile(file * fin, TipoElemLista *pelem); /* aggiunta per costrire la lista: la funzione riceve il nome di un file di testo e produce una lista con i dati del file, restituendo il puntatore all'inizio della lista */ TipoLista costruiscilistadafile (char nmf[31]); Suggerimento 2: ed ecco la funzione main() del programma LISTE8.C int main() { TipoLista list;
TipoElemLista el; char nomefile[31]; 26 printf(" - nome del file: "); scanf("%s", nomefile); list = costruiscilistadafile(nomefile); printf(" - ecco la lista:\n"); stampalista(list); printf("\nfine\n"); return 0; 5.9. eliminazion da lista (LISTE9.C) Scrivere un programma che - costruisca una lista con i dati (interi) letti da un file di testo - stampi la lista - per due volte chieda un intero da input e lo elimini dalla lista (se lo trova) - stampi di nuovo la lista - scarichi i dati in lista nello stesso file di testo il nome del file di testo deve essere ricevuto da input. Ovviamente si opera in modo da estendere il programma precedente, aggiungendo ed usando funzioni opportune. Ecco le due funzioni principali aggiunte nel file LISTE9.C /* aggiunta per eliminare un elemento da una lista */ void eliminadalista(tipolista * plis, TipoElemLista elem); /* aggiunta per scaricare i dati della lista lis nel file di nome nmf */ void listainfile (TipoLista lis, char nmf[31]); 5.10. inserimento ordinato in lista (LISTE10.C) Scrivere un programma in cui venga costruita e poi stampata una lista di interi, i cui elementi siano ordinati in senso crescente. I dati da inserire in lista vengono forniti da input come una sequenza (disordinata) di interi, terminata da 0 La soluzione da adottare consiste nel costruire la lista mediante una serie di inserimenti ordinati. Ogni inserimento viene effettuata dia una chiamata all opportuna funzione insordlista(). L algoritmo di inserimento ordinato che si consiglia e quello che fa uso del record generatore e di due puntatori di appoggio.
Ecco i prototipi delle funzioni necessarie (oltre a quelle immaginabili, gia sviluppate in esercizi precedenti). int precedeelem(tipoelemlista el1, TipoElemLista el2); /* aggiunta per determinare se un certo elemento va inserito prima o dopo di un altro: restituisce 1 se el1 precede el2 in ordinamento crescente (0 altrimenti) */ int insordlista(tipolista * plis, TipoElemLista elem); /* aggiunta per eseguire l'inserimento: inserisece elem ordinatamente in lista; plis e' l'indirizzo della variabile puntatore che punta all'inizio della lista */ 27