Programmazione I    (Canale P-Z)

Esercizi di Preparazione all'Esame con Soluzioni


Le parole chiave del linguaggio C sono evidenziate in blu, i commenti sono in rosso, le funzioni, i tipi e le costanti
della libreria standard sono evidenziate in verde. Tutto il resto è in nero.
 

Esercizio 1

Si consideri il tipo Data e il tipo Offerta così definiti:
 
typedef struct  {
    short         giorno;
    short         mese;
    short         anno;
} Data;
typedef struct {
    char        nomeProdotto[20];
    Data        scadenza;
} Offerta;
Scrivere una funzione che preso in input un vettore di offerte, la sua lunghezza e una data, elimina dal vettore compattandolo le offerte
la cui scadenza è antecedente alla data fornita in input e ritorna la nuova lunghezza. Il prototipo della funzione è:
                                                        int Aggiorna(Offerta V[], int l, Data d).

Soluzione Esercizio 1

/* Funzione ausiliaria che ritorna 1 se la prima data e' antecedente *
 * alla seconda e 0 altrimenti.                                      */
short Antecedente(Data d1, Data d2)
{
    if (d1.anno < d2.anno) return 1;
    else if (d1.anno == d2.anno && d1.mese < d2.mese) return 1;
    else if (d1.anno == d2.anno && d1.mese == d2.mese && d1.giorno < d2.giorno) return 1;
    else return 0;
}

int Aggiorna(Offerta V[], int l, Data d)
{
    int i, k = 0;
    for (i = 0 ; i < l ; i++) {
        if (!Antecedente(V[i].scadenza, d)) {  /* Se la scadenza non e' antecedente alla */
            V[k] = V[i];                       /* data d, sposta l'offerta nella prima   */
            k++;                               /* posizione libera.                      */
        }
    }
    return k;
}



 

Esercizio 2

Scrivere una funzione che preso in input il puntatore al primo elemento di una lista di stringhe ordinata, elimina i duplicati
e ritorna il puntatore alla lista così ripulita. Gli elementi della lista hanno il seguente tipo:

typedef struct SList {
    char          str[30];
    struct SList *next;
} SList;

Il prototipo della funzione è  SList *Ripulisci(SList *). La memoria degli elementi eliminati deve essere rilasciata.

Soluzione Esercizio 2

SList *Ripulisci(SList *list)
{
    if (list != NULL && list->next != NULL) {
        SList *e = list;
        do {
            if (!strcmp(e->str, e->next->str)) {   /* Se le stringhe sono uguali    */
                SList *p = e->next;                /* elimina il prossimo elemento. */
                e->next = p->next;
                free(p);
            } else e = e->next;
        } while (e->next != NULL);
    }
    return list;
}


 

Esercizio 3

Scrivere una funzione che prese in input due stringhe fileMatrice e fileColonne, crea un file di nome
fileColonne e vi scrive le somme degli elementi delle colonne della matrice di interi memorizzata nel file di
nome fileMatrice. La matrice è memorizzata in modo tale che i primi due interi sono il numero di righe e il
numero di colonne della matrice e poi seguono gli elementi della matrice disposti per righe. Entrambi i file sono di
tipo testo. Il prototipo della funzione è
      void SommaCol(const char *fileMatrice, const char *fileColonne).
Ad esempio se il contenuto del file fileMatrice è
             2 3 25 -2 13 -9 105 77
allora la matrice ha 2 righe, 3 colonne, la prima riga è 25 -2 13 e la seconda riga è  -9 105 77. La funzione deve
creare un file contenente  14 103 90.

Soluzione Esercizio 3

void SommaCol(const char *fileMatrice, const char *fileColonne)
{
    FILE *fM, *fC;
    int r, c, nr, nc, *som;
    fM = fopen(fileMatrice, "r");    /* Apri file matrice */
    fscanf(fM, "%d", &nr);           /* Leggi il numero di righe */
    fscanf(fM, "%d", &nc);           /* Leggi il numero di colonne */
    som = malloc(sizeof(int)*nc);             /* Alloca vettore per somme colonne */
    for (c = 0 ; c < nc ; c++) som[c] = 0;    /* e inizializzalo.                 */
               /* Leggi la matrice e calcola le somme delle colonne */
    for (r = 0 ; r < nr ; r++) {
      for (c = 0 ; c < nc ; c++) {           /* Leggi r-esima riga */
          int v;
          fscanf(fM, "%d", &v);
          som[c] += v;
        }
    }
    fclose(fM);
    fC = fopen(fileColonne, "w");    /* Crea ed apri il file fileColonne e */
    for (c = 0 ; c < nc ; c++)       /* scrivici le somme delle colonne.   */
        fprintf(fC, "%d", som[c]);
    fclose(fC);
    free(som);                       /* Rilascia la memoria del vettore delle somme */
}


   

Esercizio 4

Scrivere una funzione che preso in input il puntatore al primo elemento di una lista di interi ritorna la somma dei valori interi
dispari della lista. Gli elementi della lista hanno il seguente tipo:
typedef struct IntList {
    int             valore;
    struct IntList *next;
} IntList;
Il prototipo della funzione è int Somma(IntList *).

Soluzione Esercizio 4

int Somma(IntList *list)
{
    int somma = 0;
    while (list != NULL) {                 /* Scorri la lista */
        int val = list->valore;
        if ((val % 2) == 1) somma += val;  /* Se il valore e' dispari sommalo */
        list = list->next;
    }
    return somma;
}


 

Esercizio 5

Scrivere una funzione che presi in input i puntatori ai primi elementi di due liste di interi, crea una nuova lista dello stesso tipo
contenente esattamente i valori interi comuni alle due liste senza ripetizioni e ritorna il puntatore al primo elemento della lista creata.
Le due liste di input non devono essere modificate. Il tipo degli elementi è il seguente:
typedef struct List {
    int          valore;
    struct List *next;
} List;
Il prototipo della funzione è   List *Comuni(const List *, const List *).
Ad esempio se le due liste di input sono:
    2 -> 3 -> 5 -> 3 -> 5
    7 -> 2 -> 3 -> 5 -> 5 -> 7
Allora la funzione deve restituire la lista: 2 -> 3 -> 5 (l'ordine degli elementi non è importante).

Soluzione Esercizio 5

/* Funzione ausiliaria che controlla se il valore val e' presente o meno *
 * nella lista L.                                                        */
short Cerca(const List *L, int val)
{
    while (L != NULL && L->valore != val) L = L->next;
    return (L != NULL);
}

List *Comuni(const List *L1, const List *L2)
{
    List *comL = NULL;
    while (L1 != NULL) {                            /* Scorri la prima lista e per ogni  */
        int val = L1->valore;                       /* valore controlla se e' presente   */
        if (Cerca(L2, val) && !Cerca(comL, val)) {  /* nella seconda e non e' gia' stato */
            List *nuovoElem = malloc(sizeof(List)); /* aggiunto alla nuova. Se e' cosi'  */
            nuovoElem->valore = val;                /* aggiungi un nuovo elemento in     */
            nuovoElem->next = comL;                 /* testa alla nuova lista.           */
            comL = nuovoElem;
        }
        L1 = L1->next;
    }
    return comL;
}



 

Esercizio 6

Scrivere una funzione che preso in input un vettore di interi e la sua dimensione, ritorna 1 se il vettore contiene duplicati
e 0 altrimenti. Il prototipo della funzione è:  int Duplicati(int A[], int dim).
 

Soluzione Esercizio 6

int Duplicati(int A[], int dim)
{
    int i, j;
    for (i = 0 ; i < dim ; i++) {
        for (j = i + 1 ; j < dim ; j++)
            if (A[i] == A[j]) return 1;
    }
    return 0;
}


 

Esercizio 7

Si scriva una funzione con il prototipo  char *CercaStringa(const char *s1, const char *s2)
che cerca la stringa s1 come sottostringa della stringa s2. Se s1 occorre come sottostringa di s2, la funzione ritorna
il puntatore al primo carattere della prima occorrenza di s1 in s2. Altrimenti ritorna NULL.

Soluzione Esercizio 7

char *CercaStringa(const char *s1, const char *s2)
{
    int i = 0;
    do {
        int j = 0;                                       /* Controlla se la stringa s1    */
        while (s1[j] != '\0' && s1[j] == s2[i + j]) j++; /* compare in s2 a partire dalla */
        if (s1[j] == '\0') return &(s2[i]);              /* posizione i.                  */
    } while (s2[i++] != '\0');
    return NULL;
}


 

Esercizio 8

Si scriva una funzione con il prototipo   void StrTac(char *s1, const char *s2)
che restituisce nel blocco puntato da s1 la stringa formata concatenando la stringa s2 seguita dalla stringa s1.

Soluzione Esercizio 8

void StrTac(char *s1, const char *s2)
{
    int i, l1 = strlen(s1), l2 = strlen(s2);          /* Calcola lunghezze stringhe */
    for (i = l1 ; i >= 0 ; i--) s1[l2 + i] = s1[i];   /* Sposta s1 di l2 posizioni */
    for (i = 0 ; i < l2 ; i++) s1[i] = s2[i];         /* Copia s2 nelle prime l2 */
}                                                     /* posizioni.              */


 

Esercizio 9

Si scriva una funzione con il prototipo   int NumParole(const char *str, int k)
che ritorna il numero di parole di lunghezza k contenute nella stringa str. Per parola si intende una sequenza di
caratteri alfanumerici consecutivi di lunghezza massimale. Ad esempio se str è la stringa
            "L'Aquila e' un uccello. L'Aquilone no."
e se k = 8 allora la funzione deve ritornare 1 se invece k = 2 allora la funzione deve ritornare 2.
Per determinare se un carattere c è alfanumerico si può usare la funzione intisalnum(int c)
della libreria standard (<ctype.h>).

Soluzione Esercizio 9

int NumParole(const char *str, int k)
{
    short parola = 0;              /* Indica se si sta scandendo una parola o meno */
    int numP = 0, lungP = 0;
    do {
        if (isalnum(str[i])) {           /* Se e' un carattere alfanumerico allora */
            parola = 1;                  /* stiamo scandendo una parola quindi     */
            lungP++;                     /* incrementa la lunghezza della parola.  */
        } else {                                /* Altrimenti,                       */
            if (parola && lungP == k) numP++;   /* se il carattere non-alfanumerico  */
            parola = 0;                         /* termina una parola e la lunghezza */
            lungP = 0;                          /* della parola e' k, incrementa il  */
        }                                       /* numero di parole di lunghezza k.  */
    } while (str[i++] != '\0');
    return numP;
}


Esercizio 10

Scrivere una funzione che preso in input una stringa stampa per ciascuna cifra (cioè un carattere dell'insieme
0,1,2,3,4,5,6,7,8,9) presente nella stringa il numero di occorrenze. La stampa deve avvenire per cifre
crescenti. Il prototipo della funzione è  void StampaOcc(const char *).
Ad esempio se la stringa di input è  "3aB91cz33391a"  allora la funzione deve stampare:
    la cifra 1 compare 2 volte
    la cifra 3 compare 4 volte
    la cifra 9 compare 2 volte

Soluzione Esercizio 10

void StampaOcc(const char *str)
{
    int i, occ[10];                  /* Il vettore occ servira' a contare le occorrenze */
    for (i = 0 ; i <= 9 ; i++) occ[i] = 0;   /* Inizializza il vettore delle occorrenze */
    i = 0;
    while (str[i] != '\0') {     /* Scorri la stringa */
        char c = str[i];
        if (c >= '0' && c <= '9')  /* Se l'i-esimo carattere e' una cifra */
            occ[c - '0']++;        /* incrementa il numero di occorrenze. */
    }
    for (i = 0 ; i <= 9 ; i++) {   /* Per ogni cifra stampa il numero di     */
        if (occ[i] > 0)            /* occorrenze (solo se e' maggiore di 0). */
            printf("la cifra %d compare %d volte\n", i, occ[i]);
    }
}


 

Esercizio 11

Scrivere una funzione che preso in input il puntatore al primo elemento di una lista di numeri e un intero non
negativo d, elimina dalla lista gli ultimi d elementi e ritorna il puntatore al primo elemento della lista così modificata.
Se la lista ha meno di d elementi, tutti gli elementi della lista vengono eliminati. Il tipo degli elementi della lista è
typedef struct NumElem {
    float           num;
    struct NumElem *pros;
} NumElem, *NumList;
Il prototipo della funzione è   NumList CutTail(NumList L, unsigned long d).

Soluzione Esercizio 11

/* Funzione ausiliaria che elimina l'ultimo elemento della lista L *
 * e ritorna il puntatore alla lista cosi' modificata.             */
NumList DelLast(NumList L)
{
    if (L == NULL) return NULL;       /* Se la lista e' vuota non fa nulla */
    else if (L->pros == NULL) {       /* Se la lista ha un solo elemento, */
        free(L);                      /* libera la memoria dell'elemento  */
        return NULL;                  /* e ritorna la lista vuota.        */
    } else {                                       /* Se la lista ha almeno due elementi, */
        NumList p = L;                             /* cerca l'ultimo elemento, mantenendo */
        while (p->pros->pros != NULL) p = p->pros; /* il puntatore al penultimo.          */
        free(p->pros);
        p->pros = NULL;
        return L;
    }
}

NumList CutTail(NumList L, unsigned long d)
{
    for ( ; d > 0 ; d--) L = DelLast(L);
    return L;
}



 

Esercizio 12

Scrivere una funzione che presa in input una lista di stringhe, elimina dalla lista tutti i duplicati e ritorna il puntatore al primo
elemento della lista così ripulita. Gli elementi della lista hanno il seguente tipo:
typedef struct SList {
    char *        stringa;
    struct SList *next;
} SList;
La memoria degli elementi eliminati deve essere rilasciata. Per questo si assume che sia la struct SList che il campo
stringa sono stati allocati dinamicamente. Il prototipo della funzione è   SList *RipulisciStr(SList *).

Soluzione Esercizio 12

/* Funzione ausiliaria che determina se la stringa str e' presente o meno *
 * nella lista di stringhe L.                                             */
short TrovaStr(const char *str, const SList *L)
{
    while (L != NULL && strcmp(str, L->stringa) != 0) L = L->next;
    return (L != NULL);
}

SList *RipulisciStr(SList *L)
{
    SList **pL = &L;           /* Scorri la lista mantenendo in pL l'indirizzo della */
    while (*pL != NULL) {      /* locazione che contiene il puntatore all'elemento   */
        SList *elem = *pL;     /* corrente.                                          */
        if (TrovaStr(elem->stringa, elem->next)) {  /* Se vi e' un elemento successivo */
            *pL = elem->next;                       /* uguale all'elemento corrente,   */
            free(elem->stringa);                    /* elimina l'elemento corrente.    */
            free(elem);
        } else pL = &(elem->next);
    }
    return L;
}



 

Esercizio 13

Scrivere una funzione che presi in input tre interi non negativi nA, nB, nC, restituisce una stringa allocata dinamicamente
che consiste di nA caratteri A seguiti da nB caratteri B seguiti da nC caratteri C. Il prototipo della funzione è
    char *ABCStringa(unsigned short nA, unsigned short nB, unsigned short nC).
Ad esempio se nA = 2, nB = 1 e nC = 4 la funzione deve restituire la stringa "AABCCCC".

Soluzione Esercizio 13

char *ABCStringa(unsigned short nA, unsigned short nB, unsigned short nC)
{
    char *stringa = malloc((nA + nB + nC + 1)*sizeof(char));   /* Alloca la stringa */
    long n, i = 0;                /* La variabile i conterra' sempre l'indice del */
                                  /* prossimo carattere (non ancora scritto).     */
    for (n = nA ; n > 0 ; n--) stringa[i++] = 'A';       /* Scrivi le A */
    for (n = nB ; n > 0 ; n--) stringa[i++] = 'B';       /* Scrivi le B */
    for (n = nC ; n > 0 ; n--) stringa[i++] = 'C';       /* Scrivi le C */
    stringa[i] = '\0';
    return stringa;
}


 

Esercizio 14

Scrivere una funzione che presa in input una lista di interi, crea una nuova lista dello stesso tipo che contiene gli stessi
elementi della lista di input ma in ordine inverso. Il tipo degli elementi è il seguente:
typedef struct IntList {
    int             data;
    struct IntList *next;
} IntList;
Il prototipo della funzione è   IntList *CreaInversa(const IntList *).
Ad esempio se la lista di input è 2 -> 5 -> 2 -> 9 -> 13 la lista restituita dalla
funzione è 13 -> 9 -> 2 -> 5 -> 2.

Soluzione Esercizio 14

IntList *CreaInversa(const IntList *L)
{
    IntList *nuovaL = NULL;
    while (L != NULL) {                         /* Scorri la lista e per ogni elemento   */
        IntList *e = malloc(sizeof(IntList));   /* crea un nuovo elemento uguale e       */
        e->data = L->data;                      /* aggiungilo in testa alla nuova lista. */
        e->next = nuovaL;
        nuovaL = e;
        L = L->next;
    }
    return nuovaL;
}


 

Esercizio 15

Scrivere una funzione che preso in input una stringa nomeFile, legge dal file di nome nomeFile gli interi contenuti in esso,
crea una lista contenente tali interi (non necessariamente nello stesso ordine) e ritorna il puntatore al primo elemento della lista
creata. Il tipo degli elementi della lista è così definito:
typedef struct Elem {
    int          intero;
    struct Elem *pross;
} Elem, *Lista;
Il file si assume essere di tipo testo. Il prototipo della funzione è  Lista File2Lista(const char *nomeFile).
Ad esempio se il file contiene 13 -6 45 897 allora la funzione restituisce la lista 13 -> -6 -> 45 -> 897 (l'ordine
non ha importanza).

Soluzione Esercizio 15

Lista File2Lista(const char *nomeFile)
{
    Lista L = NULL;                    /* Inizializza la lista a vuota */
    FILE *f = fopen(nomeFile, "r");    /* Apri il file in sola lettura */
    int val;
    while (
fscanf(f, "%d", &val) == 1) {  /* Leggi il prossimo intero dal file */
        Elem *e = malloc(sizeof(Elem));   /* Alloca un nuovo elemento che conterra' */
        e->intero = val;                  /* l'intero letto dal file.               */
        e->pross = L;                     /* Aggiungi l'elemento in testa alla lista */
        L = e;
    }
    return L;
}


 

Esercizio 16

Scrivere una funzione che preso in input un vettore di interi e la sua dimensione, riordina gli elementi del vettore in senso inverso.
Il prototipo della funzione è   void Inverti(int V[], int dim). Ad esempio se V = [2, -7, 5, 2, 76],
la funzione lo inverte così V = [76, 2, 5, -7, 2].

Soluzione Esercizio 16

void Inverti(int V[], int dim)
{
    int i = 0, j = dim - 1;    /* L'indice i procede dall'inizio verso la fine del */
    while (i < j) {            /* vettore e l'indice j dalla fine verso l'inizio.  */
        int c = V[i];          /* Scambia V[i] e V[j] */
        V[i] = V[j];
        V[j] = c;
        i++;
        j--;
    }
}


 

Esercizio 17

Diciamo che un vettore V di n interi è palindromose la sequenza di interi V[0], V[1], ..., V[n - 1] è  uguale alla
sequenza inversa V[n - 1], V[n - 2], ..., v[0]. Scrivere una funzione che preso in input un vettore di interi e la
sua dimensione ritorna 1 se il vettore è palindromo e 0 altrimenti. Il prototipo della funzione è
                     short Palindromo(int V[], int dim).

Soluzione Esercizio 17

short Palindromo(int V[], int dim)
{
    int i = 0, j = dim - 1;    /* L'indice i procede dall'inizio verso la fine del */
    while (i < j) {            /* vettore e l'indice j dalla fine verso l'inizio.  */
        if (V[i] != V[j])        /* Se V[i] e' diverso da V[j] allora */
            return 0;            /* non e' palindromo.                */
        i++;
        j--;
    }
    return 1;
}


 

Esercizio 18

Scrivere una funzione che preso in input un vettore di interi ordinato in senso non decrescente e la sua dimensione, elimina
dal vettore tutti i duplicati compattandolo e ritorna il numero di elementi eliminati. Il prototipo della funzione è
                  long EliminaDuplicati(int V[], long dim).
Ad esempio se V = [2, 4, 4, 5, 7, 7, 7] la funzione modifica il vettore in modo tale che i primi 4 elementi del
vettore siano [2, 4, 5, 7] e ritorna 3.

Soluzione Esercizio 18

long EliminaDuplicati(int V[], long dim)
{
    int k = 0, i;
    for (i = 1 ; i < dim ; i++) {
        if (V[i] != V[k]) {
            k++;
            V[k] = V[i];
        }
    }
    return dim - (k + 1);
}


 

Esercizio 19

Scrivere una funzione che preso in input una lista di interi, per ogni elemento della lista modifica l'intero in esso contenuto in
modo tale che contenga la somma di tutti gli interi contenuti nella lista a partire da quel elemento. Il tipo degli elementi
della lista è:
typedef struct IntElem {
    int             intero;
    struct IntElem *next;
} IntElem, *IntList;
Il prototipo della funzione è    void SomList(IntList). Ad esempio se la lista è 2 -> 5 -> 3 -> 13 allora
la funzione modifica la lista così 23 -> 21 -> 16 -> 13.

Soluzione Esercizio 19

/* Funzione ausiliaria che ritorna la somma degli interi della lista L */
int Somma(const IntElem *L)
{
    int s = 0;
    while (L != NULL) {
        s += L->intero;
        L = L->next;
    }
    return s;
}

void SomList(IntList L)
{
    while (L != NULL) {
        L->intero = Somma(L);
        L = L->next;
    }
}



 

Esercizio 20

Scrivere una funzione che presi in input una lista di interi e una stringa nomeF crea un file di tipo testo di nome nomeF e
vi scrive gli interi che nella lista compaiono un numero dispari di volte. Il tipo degli elementi della lista è:
typedef struct Elem {
    int          valore;
    struct Elem *succ;
} Elem;
Il prototipo della funzione è    void FileDispari(const Elem *L, const char *nomeF). Ad esempio se
la lista è 3 -> 2 -> 3 -> 5 -> 4 -> 5 -> 5 il file deve contenere 2 5 4 (l'ordine non ha importanza).

Soluzione Esercizio 20

/* Funzione ausiliaria che ritorna il numero di volte che l'intero x compare *
 * nella lista L.                                                            */
int Occ(int x, const Elem *L)
{
    int occ = 0;
    while (L != NULL) {
        if (L->valore == x) occ++;
        L = L->succ;
    }
    return occ;
}

void FileDispari(const Elem *L, const char *nomeF)
{
    FILE *f = fopen(nomeF, "w");    /* Crea ed apri il file */
    const Elem *e = L;
    while (e != NULL) {
        int val = e->valore; /* Se l'intero dell'elemento corrente compare un numero *
                              * dispari di volte e questa e' l'ultima occorrenza,    */
        if ((Occ(val, L) % 2) == 1 && Occ(val, e->succ) == 0)              /* allora */
            fprintf(f, "%d ", val);                     /* scrivi l'intero nel file. */
        e = e->succ;
    }
    fclose(f);
}


Esercizio 21

Scrivere una funzione, con prototipo char *DelChar(const char *string, char del) che ritorna una stringa,
allocata dinamicamente, ottenuta eliminando dalla stringa string tutte le eventuali occorrenze del carattere del. La stringa
ritornata non deve usare più memoria di quella strettamente necessaria.
Ad esempio DelChar("programma", 'a') ritorna la stringa "progrmm".

Soluzione Esercizio 21

char *DelChar(const char *string, char del)
{
    char *newStr;
    long i, j, ndc;
    for (ndc = 0, i = 0 ; string[i] != '\0' ; i++)       //Calcola il numero di caratteri
        if (string[i] == del) ndc++;                     //da eliminare.
                //Alloca lo spazio di memoria strettamente necessario per la nuova stringa
    newStr = malloc((strlen(string) - ndc + 1)*sizeof(char));
    for (j = 0, i = 0 ; string[i] != '\0' ; i++)         //Copia i caratteri diversi da del
        if (string[i] != del) newStr[j++] = string[i];   //nella nuova stringa.
    newStr[j] = '\0';
    return newStr;
}



Esercizio 22 

Si consideri il seguente tipo:
typedef struct MultiElem {
    short             num;
    long              count;
    struct MultiElem *next;
} MultiElem, *MultiSet;

Scrivere una funzione, con prototipo void Compact(MultiSet M), che elimina dalla lista M tutti gli elementi h per cui
esiste un elemento k che precede h e che ha lo stesso valore del campo num (k.num = h.num), inoltre per ogni elemento k non
eliminato il campo count di k deve contenere la somma dei valori dei campi count degli elementi della lista originale che avevano lo
stesso valore del campo num di k. L'ordine degli elementi non deve essere cambiato e la memoria degli elementi eliminati deve essere
rilasciata. Ad esempio se la lista di input è
{233, 2} -> {457, 1} -> {233, 1} -> {132, 5} -> {132, 3} -> {233, 8}

allora la funzione modifica la lista così {233, 11} -> {457, 1} -> {132, 8}.

Soluzione Esercizio 22

/* Funzione ausiliaria che elimina da M tutti gli elementi (oltre il primo) che hanno
   nel campo num lo stesso valore del campo num del primo elemento di M e ritorna la
   somma dei valori del campo count degli elementi eliminati. Se la lista e' vuota o
   non vengono eliminati elementi ritorna 0.                                         */

long DelNext(MultiSet M)
{
    long sumCount = 0;
    if (M != NULL) {
        int num = M->num;
        while (M->next != NULL) {          //Scorri la lista a partire dal secondo elemento
           
MultiElem *nextE = M->next;
            if (nextE->num == num) {                 //Se l'elemento ha lo stesso valore del
               
MultiElem *nextnextE = nextE->next;  //campo num, eliminalo.
                sumCount += nextE->count;
                free(nextE);
                M->next = nextnextE;
            }
            M = M->next;
        }
    }
    return sumCount;
}

void Compact(MultiSet M)

{
    while (M != NULL) {
        M->count += DelNext(M);
        M = M->next;
    }
}


Esercizio 23

Scrivere una funzione, con prototipo long CatFiles(char *conF, char *catF), che appende il contenuto del
file il cui nome è nella stringa catF al file il cui nome è nella stringa conF e ritorna il numero di caratteri del primo file dopo
l'aggiunta. Entrambi i files si assumono di tipo testo.
Ad esempio, se il contenuto del file conF è  Gli?esami?
  e il contenuto del file catF  è  non?finiscono?mai
allora la funzione rende il contenuto del file conF uguale a  Gli?
esami?non?finiscono?mai  e ritorna 27.

Soluzione Esercizio 23

long CatFiles(char *conF, char *catF)
{
    FILE *fcon = fopen(conF, "r+");      //Apri il file conF in lettura e scrittura
    FILE *fcat = fopen(catF, "r");       //Apri il file catF in lettura
    long nc = 0;
    int c;
    while
(fgetc(fcon) != EOF) nc++;     //Calcola il numero di caratteri di conF
    while ((c = fgetc(fcat)) != EOF) {   //Aggiungi il contenuto di catF a conF
        fputc(c, fcon);
        nc++;
    }
    fclose(fcon);
    fclose(fcat);
    return nc;
}


Esercizio 24

Scrivere una funzione che presa in input una lista di stringhe crea una stringa uguale alla concatenazione delle stringhe
della lista. Il tipo degli elementi della lista è il seguente:
typedef struct StrElem {
    char *          str;
    struct StrElem *next;
} StrElem;
Il prototipo della funzione è char *ConcatList(const StrElem *). Ovviamente la stringa ritornata deve essere
allocata dinamicamente. Ad esempio se la lista è  "List" -> "a di es" -> "" -> "e" -> "mpio" allora la
funzione deve restituire la stringa "Lista di esempio".

Soluzione Esercizio 24

char *ConcatList(const StrElem *L)
{
    char *stringa;
    const StrElem *p = L;
    long lung = 0;               /* Calcola la lunghezza della stringa che */
    while (p != NULL) {          /* e' uguale alla somma delle lunghezze   */
        lung += strlen(p->str);  /* delle stringhe della lista.            */
        p = p->next;
    }
    stringa = malloc((lung + 1)*sizeof(char));   /* Alloca la stringa e    */
    stringa[0] = '\0';                           /* inizializzala a vuota. */
    p = L;
    while (p != NULL) {            /* Concatena le stringhe della lista */
        strcat(stringa, p->str);   /* copiandole nella stringa.         */
        p = p->next;
    }
    return stringa;
}

Esercizio 25

Data una sequenza di numeri diciamo che un numero x della sequenza è un massimo se sia il numero che precede x che quello che
lo segue sono strettamente minori di x (né il primo né l'ultimo numero della sequenza può essere un massimo). Scrivere una funzione
che presa in input una lista di numeri restituisce un vettore contenente i massimi della lista (nell'ordine in cui appaiono nella lista).
Il tipo degli elementi della lista è il seguente:
typedef struct ENum {
    float        n;
    struct ENum *next;
} ENum;

Il prototipo della funzione è float *Massimi(const ENum *L, long *dim). In dim restituisce la dimensione del vettore
ritornato (che deve essere allocato dinamicamente). Il vettore non deve essere sovradimensionato. Ad esempio se la lista è
12 -> 3 -> 7 -> 4 -> 3 -> 2 -> 5 -> 1 -> 8 -> 8
allora la funzione ritorna il vettore [7, 5] e in dim restituisce 2.

Soluzione Esercizio 25

/* Funzione ausiliaria che ritorna il puntatore al primo massimo della lista L. *
 * Se non ci sono massimi ritorna NULL.                                         */

ENum *PrimoMax(const ENum *L)
{
    if (L == NULL || L->next == NULL) return NULL;
    else {
        float prec = L->n;
        L = L->next;
        while (L->next != NULL && (L->n <= prec || L->n <= L->next->n)) {
            prec = L->n;
            L = L->next;
        }
        return (L->next != NULL ? L : NULL);
    }
}

float *Massimi(const ENum *L, long *dim)
{
    float *vetMax = NULL;
    long nMax = 0;
    const ENum *e = L;
    while ((e = PrimoMax(e)
) != NULL) nMax++;   /* Conta i massimi */
    if (nMax > 0) {
        long i = 0;
        vetMax = malloc(nMax*sizeof(float));   /* Alloca il vettore dei massimi */
        while ((L = PrimoMax(L)) != NULL) vetMax[i++] = L->n;
    }
    *dim = nMax;
    return vetMax;
}


Esercizio 26

Si consideri il seguente tipo:
typedef struct Elem {
    int            val;
    struct Elem *  next;  
} Elem, *List;

Scrivere una funzione, con prototipo List Twist(List A, List B), che fonde le due liste di input in una lista L che se
A   è a1 -> a2 -> a3 -> ecc.   e  B   è  b1 -> b2 -> b3 -> ecc.   allora  L   è
a1 -> b1 -> a2 -> b2 -> a3 -> b3 ->
ecc. La funzione ritorna il puntatore alla lista L. La lista L non deve usare
memoria in più rispetto a quella usata da A e B. Se A è vuota la funzione ritornerà B e se B è vuota ritornerà A. Se le due liste hanno
lunghezze differenti, gli elementi rimanenti della lista più lunga vengono accodati.
Ad esempio se A è la lista 100 -> 101 -> 102  e  B  è la lista  1 -> 2 -> 3 -> 4 -> 5   allora L sarà la lista
100 -> 1 -> 101 -> 2 -> 102 -> 3 -> 4 -> 5.

Soluzione Esercizio 26

List Twist(List A, List B)
{
    if (A == 
NULLreturn B;
    List L = A;
    while (A != NULL && B != NULL) {            //Finche' le due liste non sono vuote
        List nextA = A->next, nextB = B->next;        //Salva i prossimi elementi
        A->next = B;                                  //Accoda un elemento di B
        if (nextA != NULL)                            //Se A non e' terminata allora
           
B->next = nextA;                          //accoda un elemento di A.
        A = nextA;
        B = nextB;
    }
    return L;
}


Esercizio 27

Scrivere una funzione, con prototipo int **MTri(long n), che crea e restituisce una matrice triangolare di interi (int) i cui
elementi hanno valori 1,2,3,..., n(n+1)/2. Naturalmente, la matrice deve essere allocata dinamicamente e non bisogna usare
più memoria di quella strettamente necessaria. Ad esempio se n = 5 la funzione deve creare la matrice:
1  2  3  4  5
6  7  8  9 
10 11 12
13 14
15
.

Soluzione Esercizio 27

int **MTri(long n)
{
    int **M = malloc(n*sizeof(int *));    //Alloca il vettore delle righe
    long r, c, nc = n, k = 1;
    for (r = 0 ; r < n ; r++) {           //Per ogni riga della matrice...
        M[r] = malloc(nc*sizeof(int));        //Alloca la riga r-esima
        for (c = 0 ; c < nc ; c++) {          //Assegna i valori agli elementi della
            M[r][c] = k;                      //riga r-esima.
            k++;
        }
        nc--;
    }
    return M;
}


Esercizio 28

Scrivere una funzione, con prototipo void Paste(char *fName, unsigned n, const char *str, char *fOut), che crea
un file di tipo testo, il cui nome è nella stringa fOut, il cui contenuto è dato da i primi n caratteri del file il cui nome è nella stringa fName seguiti
dai caratteri della stringa str ed infine seguiti dai caratteri del file fName oltre l'n-esimo. Se n = 0 il contenuto del file fOut è dato dai
caratteri di str seguiti da tutti i caratteri del file fName. Se n è maggiore od uguale alla lunghezza del file fName allora fOut conterrà i
caratteri del file fName seguiti dai caratteri della stringa str. Ad esempio se il file fName contiene:
L'esame del 2004.
ed n = 12, la stringa str è "7 giugno ", allora il file fOut conterrà:
L'esame del 7 giugno 2004.

Soluzione Esercizio 28

void Paste(char *fName, unsigned n, const char *str, char *fOut)
{
    FILE *f = fopen(fName, "r");            //Apri il file fName in lettura
    FILE *fO = fopen(fOut, "w+");           //Crea ed apri il file fOut
    int c;
    while (n > 0 && (c = fgetc(f)) != EOF) {    //Leggi i primi n caratteri del file
        fputc(c, fO);                           //fName e scrivili nel file fOut.
        n--
    }
    fprintf(fO, "%s", str);                     //Scrivi la stringa str nel file fOut
    while ((c = fgetc(f)) != EOF)               //Scrivi i rimanenti caratteri del file
        fputc(c, fO);                           //fName nel file fOut.
    fclose(f);
    fclose(fO);
}


Esercizio 29

Scrivere una funzione che presi in input due vettori di interi A e B e le loro dimensioni, elimina dal vettore A
compattandolo tutti i valori che non sono presenti anche in B e ritorna il numero di valori rimasti in A. Il
prototipo della funzione è long Com(int A[], long dimA, int B[], long dimB). Ad esempio
se A = [2, 7, 2, 4, 9, 9, 5] (dimA = 7)  e  B = [5, 5, 4, 6, 2] (dimB = 5) allora la
funzione modifica il vettore A in modo tale che nelle prime 4 posizioni di A vi sia [2, 2, 4, 5] e ritorna 4.

Soluzione Esercizio 29

long Com(int A[], long dimA, int B[], long dimB)
{
    long k = 0, i;         /* La variabile k manterra' l'indice della prima */
                           /* posizione disponibile del vettore A.          */
    for (i = 0 ; i < dimA ; i++) {
        long j = 0;
        while (j < dimB && A[i] != B[j]) j++;  /* Controlla se A[i] appare in B */
        if (A[i] == B[j]) {                    /* Se e' cosi' sposta A[i] nella */
            A[k] = A[i];                       /* prima posizione disponibile,  */
            k++;                               /* compattando cosi' il vettore. */
        }
    }
    return k;
}


Esercizio 30

Scrivere una funzione che presi in input una lista L, un vettore di interi V e la sua dimensione dim, elimina dalla lista tutti gli elementi che si
trovano in una posizione k tale che k < dim e V[k] = 0 e ritorna la lista così modificata. Per posizione di un elemento si intende il
numero di elementi che lo precedono, cioè, il primo elemento ha posizione 0, il secondo ha posizione 1 e così via. Il tipo degli elementi
della lista è
typedef struct Elem {
    long         v;
    struct Elem *p;
} Elem, *List;
La memoria degli elementi eliminati deve essere rilasciata. Il prototipo della funzione è List Elimina(List L, int V[], int dim).
Ad esempio se la lista è 15 -> 23 -> 5 -> 8 -> 76 ->13 e il vettore [0, 1, 0, 0, 1] (dim = 5) allora la funzione
modifica la lista così 23 -> 76 -> 13.

Soluzione Esercizio 30

List Elimina(List L, int V[], int dim)
{
    List *cur = &L;       /* Puntatore alla locazione che contiene l'indirizzo */
                          /* dell'elemento corrente.                           */
    int k = 0;
    while (*cur != NULL && k < dim) {
        Elem *e = *cur;
        if (V[k] == 0) {        /* Se V[k] e' 0 elimina dalla lista l'elemento */
            *cur = e->next;     /* in posizione k.                             */
            free(e);
        } cur = &(e->next);
        k++;
    }
    return L;
}

Esercizio 31

Scrivere una funzione che preso in input il nome di un file, contenente una sequenza di stringhe, alloca dinamicamente un vettore di stringhe
che contiene le stringhe del file che compaiono almeno due volte, e restituisce il vettore e la sua dimensione. Il file è di tipo testo e la
lunghezza
massima delle stringhe è 100. Però la lunghezza media è 7 per cui il vettore allocato non deve sprecare memoria.
Il prototipo della funzione
è  char **FileStrRip(const char *fStr, long *dim). La funzione quindi ritorna il
puntatore al vettore di stringhe e in dim restituisce la dimensione del vettore (cioè il numero delle stringhe che si ripetono almeno due volte).
Si  sottolinea che sia il vettore che le stringhe (i cui puntatori sono contenuti nel vettore) devono essere allocati dinamicamente. Ad esempio
se il file contiene
      rosso verde viola bruno rosso marrone giallo viola blu viola  
la funzione deve restituire un vettore di stringhe di dimensione 2 contenente le stringhe "rosso", "viola".

Soluzione Esercizio 31

/* Funzione ausiliaria che ritorna la stringa k-esima del file f se questa non *
 * compare in posizioni precedenti alla k-esima e compare in qualche posizione *
 * successiva alla k-esima, altrimenti ritorna NULL.                           */
const char StrRip(FILE *f, int k)
{
    static char str[101];
   
char s[101];
    int j = 0;
   
rewind(f);                /* Riporta il file all'inizio */
   
while (j < k && fscanf(f, "%100s", str) == 1)  /* Scorri le stringhe del file fino alla */  
        j++;                                                                                                  /* k-esima.                              */
    if (j != k) return NULL/* Se il file ha meno di k stringhe, ritorna NULL */
            /* A questo punto in str c'e' la k-esima stringa */
   
rewind(f);                /* Riporta il file all'inizio */
    j = 0;
    while (fscanf(f, "%100s", s)
== 1) {
        j++;
        if (strcmp(s, str) == 0) {
            if (j < k)          
/* La stringa compare anche in una    */
                return NULL;     /* posizione precedente alla k-esima. */
            else if (j > k)      /* La stringa compare anche in una    */
                return str;      /* posizione successiva alla k-esima. */
        }
    }
    return NULL;

}

char **FileStrRip(const char *fStr, long *dim)
{
    char **vetStr, *str;
    FILE *f = fopen(fStr, "r");   /* Apri il file (di tipo testo) in sola lettura */
    int i, j, nRipStr = 0, nStr = 0;
    char s[101];
    while (fscanf(f, "%100s", s) == 1)    /* Leggi il file per calcolare il numero di */
        nStr++;                           /* stringhe in esso contenute.              */
    for (j = 1 ; j <= nStr ; j++) {           /* Calcola il numero di stringhe ripetute */
        if (
StrRip(f, j) != NULL) nRipStr++;
    }

    vetStr = malloc(nRipStr*sizeof(char *));  /* Alloca il vettore di stringhe */
    i = 0;
    for
(j = 1 ; j <= nStr ; j++
) {           /* Per ogni stringa del file ...     */ 
        char *str;                            /* se e' una stringa ripetuta allora */
        if ((str = StrRip(f, j)) != NULL) {   /* alloca un blocco per la stringa.  */
            vetStr[i] = malloc((strlen(str) + 1)*sizeof(char));
            strcpy(vetStr[i], str);                
            i++;

        }

    }
    fclose(f);
    *dim = nRipStr;
    return vetStr;
}

Esercizio 32

Scrivere una funzione, con prototipo void SMove(char *str, int k), che modifica la stringa str spostando i
primi k caratteri in coda alla stringa. Ovviamente se k è maggiore od uguale alla lunghezza di str o k ≤ 0 allora SMove
non fa nulla. Ad esempio se str è "abcdefg" e k = 3 allora SMove modifica str così "defgabc".

Soluzione Esercizio 32

void SMove(char *str, int k)
{
    int len = strlen(str);
    if (k < len && k > 0) {
        char head[k];        //Vettore per salvare i primi k caratteri
        int i, j;
        for (i = 0 ; i < k ; i++)            
//Salva i primi k caratteri
            head[i] = str[i];   
        for (j = 0 ; i < len ; i++, j++)      //Sposta i caratteri successivi
            str[j] = str[i];
        for (i = 0 ; j < len ; i++, j++)      //Copia i k caratteri in coda
            str[j] = head[i];
    }
}

Esercizio 33

Si consideri il seguente tipo:
typedef struct Elem {
    long         count;
    char *       str;        //Stringa allocata dinamicamente
    struct Elem *next;
} Elem, *List;

Scrivere una funzione, con prototipo List Clean(List L, const char *test), che presa in input una lista L, ordinata
in senso non decrescente rispetto al campo count, la modifica eliminando tutti gli elementi che hanno la stringa del campo str
uguale alla stringa test (ve ne possono essere più d'uno) e inserisce un nuovo elemento con campo count pari al numero di
elementi eliminati e il campo str contenente una stringa uguale alla stringa test, mantenendo l'ordinamento della lista. La funzione
ritorna il puntatore al primo elemento della lista così modificata. La memoria degli elementi eliminati deve essere interamente rilasciata.
Ad esempio se la lista L è {0, "blu"}->{1, "rosso"}->{3, "viola"}->{3, "verde"}->{4, "rosso"}
e la stringa test è "rosso" allora Clean modifica la lista così
{0, "blu"}->{2, "rosso"}->{3, "viola"}->{3, "verde"}.

Soluzione Esercizio 33

List Clean(List L, const char *test)
{
    List *pCurr = &L;    //Puntatore alla locazione che contiene il puntatore
                         //all'elemento corrente.

    long n = 0;
    while (*pCurr != NULL) {    //Scorri la lista per eliminare eventuali elementi
        if (strcmp((*pCurr)->str, test) == 0) {    //E' un elemento da eliminare
            Elem *e = *pCurr;         //Salva il puntatore all'elemento da eliminare
            *pCurr = (*pCurr)->next;  //Elimina l'elemento dalla lista
            free(e->str);             //Rilascia la memoria della stringa
            free(e);                  //Rilascia la memoria dell'elemento
            n++;                  //Incrementa il conteggio degli elementi eliminati
        } else pCurr = &((*pCurr)->next);
    }
    Elem *new = malloc(sizeof(Elem));     //Alloca memoria per il nuovo elemento
    new->count = n;
    new->str = malloc(sizeof
(char)*(strlen(test) + 1));  //Alloca memoria per stringa
    strcpy(new->str, test);
    *pCurr = &L;                                    //Scorri la lista per trovare
    while (*pCurr != NULL && (*pCurr)->count <= n)  //dove va inserito il nuovo
        pCurr =
&((*pCurr)->next);                  //elemento.
    new->next = *pCurr;                   //Inserisci il nuovo elemento nella lista
    *pCurr = new;
    return L;
}


Esercizio 34

Scrivere una funzione, con prototipo char Matrix(char *fName, int row, int col, char newC), che scrive il
carattere newC nell'elemento posto nella riga row e colonna col della matrice memorizzata nel file di tipo testo il cui nome è nella
stringa fName e ritorna il carattere precedente di tale elemento. La matrice è una matrice di caratteri ed è memorizzata per righe, i primi
due interi del file rappresentano rispettivamente il numero di righe e il numero di colonne della matrice. Tali interi sono separati da un
carattere spazio e il primo carattere dopo il secondo intero è il primo carattere della matrice. La matrice può essere molto grande per cui la
funzione non deve copiare la matrice in memoria. Ad esempio se il file fName contiene:
4 3ABCFFFMNLVVV
(si tratta quindi di una matrice con 4 righe e 3 colonne) e se row = 3, col = 1 e newC = 'A' allora Matrix modifica il file così:
4 3ABCFFFANLVVV
e ritorna il carattere 'M'.

Soluzione Esercizio 34

/* Funzione ausiliaria che posiziona il cursore del file f pronto per leggere/scrivere
   l'elemento in riga row e colonna col.                                              */

void Elem(FILE *f, int row, int col)
{
    int nr, nc, i;
    char c;
    rewind(f);
    fscanf(f, "%d", &nr);        //Leggi il numero di righe
    fscanf(f, " %d", &nc);       //Leggi il numero di colonne
    for ( ; row > 1 ; row--)          //Scorri le prime row - 1 righe
        for (i = 1 ; i <= nc ; i++)
            fscanf(f, "%c", &c);
    for (i = 1 ; i < col ; i++)       //Scorri la riga row fino alla colonna col - 1
        fscanf(f, "%c", &c);
}
char
Matrix(char *fName, int row, int col, char newC)
{
    FILE *f = fopen(fName, "r+");   //Apri il file di tipo testo in lettura e scrittura
    char c;
    Elem(f, row, col);        //Posizionati sull'elemento di riga row e colonna col
    fscanf(f, "%c", &c);      //Leggi il carattere dell'elemento
    Elem(f, row, col);           
//Posizionati sull'elemento di riga row e colonna col
    fprintf(f, "%c", newC);       //Scrivi il nuovo carattere dell'elemento
    fclose(f);
    return c;
}


Esercizio 35

Scrivere una funzione che preso in input il nome di un file, contenente una sequenza di stringhe, alloca dinamicamente un vettore di stringhe
che contiene le stesse stringhe del file e nello stesso ordine, e restituisce il vettore e la sua dimensione. Il file è di tipo testo e la lunghezza
massima delle stringhe è 100. Però la lunghezza media è 7 per cui il vettore allocato non deve sprecare memoria. Il prototipo della funzione
è  char **FileStr2VetStr(const char *fStr, long *dim). La funzione quindi ritorna il puntatore al vettore di stringhe
e in dim restituisce la dimensione del vettore (cioè il numero delle stringhe). Si  sottolinea che sia il vettore che le stringhe (i cui puntatori
sono contenuti nel vettore) devono essere allocati dinamicamente.

Soluzione Esercizio 35

char **FileStr2VetStr(const char *fStr, long *dim)
{
    char **vetStr;
    FILE *f = fopen(fStr, "r");    /* Apri il file (di tipo testo) in sola lettura */
    long i, nStr = 0;
          char str[101];
    while (fscanf(f, "%100s", str) == 1) {   /* Leggi il file per calcolare il numero di */
        nStr++;                              /* stringhe in esso contenute.              */
    vetStr = malloc(nStr*sizeof(char *));    /* Alloca il vettore di stringhe */
    rewind(f);                               /* Riporta il file all'inizio */
    i = 0;
    while (fscanf(f, "%100s", str) == 1) {   /* Leggi di nuovo le stringhe del file */
        vetStr[i] = malloc((strlen(str) + 1)*sizeof(char)); /* Alloca la i-esima stringa */
        strcpy(vetStr[i], str);
        i++;
    }
    fclose(f);
    *dim = nStr;
    return vetStr;
}

Esercizio 36

Scrivere una funzione che presa in input una lista di interi e un intero positivo k, restituisce un vettore allocato dinamicamente che
contiene i k interi più piccoli della lista. Il tipo degli elementi della lista è così definito:
typedef struct Elem {
    int          val;
    struct Elem *next;
} Elem;
Il prototipo della funzione è int *Piccoli(const Elem *L, unsigned k). Si assume che gli interi della lista sono
tutti distinti e che sono in numero maggiore di k. Si richiede che il vettore sia ordinato in senso crescente. Ad esempio se la lista
è 6 -> 2 -> 7 -> 9 -> 5 -> 3 -> 10   e   k = 3 allora la funzione deve restituire il vettore [2, 3, 5].

Soluzione Esercizio 36

/* Funzione ausiliaria che ritorna il piu' piccolo intero strettamente maggiore di x *
 * contenuto nella lista L (se non esiste ritorna x).                                */
int MinMagg(int x, const Elem *L)
{
    int mm = x;
    while (L != NULL) {
        int v = L->val;
        if (v > x && (mm == x || v < mm)) mm = v;
        L = L->next;
    }
    return mm;
}

int *Piccoli(const Elem *L, unsigned k)
{
    int *vet = malloc(k*sizeof(int));  /* Alloca il vettore */
    const Elem *p = L;
    int i, min = L->val;               /* Scorri la lista per trovare il minimo */
    while (p != NULL) {
        if (p->val < min) min = p->val;
        p = p->next;
    }
    vet[0] = min;
    for (i = 1 ; i < k ; i++)             /* Trova il prossimo intero piu' piccolo */
        vet[i] = MinMagg(vet[i - 1], L);  /* tramite la funzione MinMagg.          */
    return vet;
}


Esercizio 37

Scrivere una funzione che presa in input una lista e un intero positivo k modifica la lista spostando il k-esimo elemento in coda alla lista
e ritorna il puntatore alla lista modificata. Se la lista ha meno di k elementi la funzione non fa nulla. Il tipo degli elementi della lista è
typedef struct Elem {
    int          info;
    struct Elem *next;
} Elem, *List;
Il prototipo della funzione è List Sposta(List L, unsigned k). Ad esempio se la lista è 7 -> 4 -> 2 -> 8 -> 9  e
k = 3 allora la funzione modifica la lista così 7 -> 4 -> 8 -> 9 -> 2.

Soluzione Esercizio 37

List Sposta(List L, unsigned k)
{
    List *p = &L;                  /* Vai al k-esimo elemento mantenendo in  */
    while (*p != NULL && k > 1) {  /* p l'indirizzo della locazione che      */
        p = &((*p)->next);         /* contiene il puntatore a tale elemento. */
        k--;
    }
    if (*p != NULL) {                        /* Se il k-esimo elemento esiste */
        Elem *e = *p;                        /* toglilo dalla lista,          */
        *p = e->next;
        while (*p != NULL) p = &((*p)->next);  /* arriva alla fine della lista  */
        *p = e;                                /* e aggancia l'elemento.        */
        e->next = NULL;
    }
    return L;
}

Esercizio 38

Scrivere una funzione, con prototipo
char *PadString(const char *str, unsigned start, unsigned end, char pad),
che ritorna una stringa, allocata dinamicamente, formata da start caratteri pad seguiti dai caratteri della stringa str ed infine
seguiti da end caratteri pad. Ad esempio PadString("giorno", 3, 2, '*') ritorna la stringa "***giorno**".

Soluzione Esercizio 38

char *PadString(const char *str, unsigned start, unsigned end, char pad)
{
    int k;                                               // Alloca la nuova stringa
    char *newStr = malloc((strlen(str) + start + end + 1)*sizeof(char));
    for (k = 0 ; k < start ; k++) newStr[k] = pad;       // Scrivi i caratteri pad iniziali
    for ( ; *str != '\0' ; str++, k++) newStr[k] = *str; // Copia i caratteri di str
    for ( ; end > 0 ; end--, k++) newStr[k] = pad;       // Scrivi i caratteri pad finali
    newStr[k] = '\0';
    return newStr;
}


Esercizio 39

Scrivere una funzione, con prototipo void PrintNumOcc(const char *str1, const char *str2), che per ogni
carattere distinto in str1 stampa il numero di occorrenze di quel carattere in str2. Ad esempio,
PrintNumOcc("pennino", "penna") deve stampare:
p 1
e 1
n 2
i 0
o 0

Soluzione Esercizio 39

/* Funzione ausiliaria che ritorna il numero di occorrenze del carattere c
   nei primi n caratteri di s.                                             */

long CountOcc(const char *s, long n, char c)
{
    long k, nOcc = 0;
    for (k = 0 ; k < n ; k++) if (s[k] == c) nOcc++;
    return nOcc;
}

void PrintNumOcc(const char *str1, const char *str2)
{
    long k, len2 = strlen(str2);
    for (k = 0 ; str1[k] != '\0' ; k++) {    // Per ogni carattere c di str1...
        char c = str1[k];
        if (CountOcc(str1, k, c) == 0)       // Se e' la prima occorrenza di c in str1,       
            printf("%c %ld\n", c, CountOcc(str2, len2, c));   // stampa il numero di volte

    }                                                         // che c occorre in str2.
}


Esercizio 40

Scrivere una funzione, con prototipo void InvFile(char *nameF, char *newF), che legge il file di tipo testo il cui nome
è nella stringa nameF, contenente una sequenza di numeri interi separati da spazi, e crea un nuovo file di testo, con nome dato
dalla stringa newF, e vi scrive la sequenza inversa. Ad esempio, se il file nameF contiene la sequenza 3 7 -2 15 allora il nuovo
file creato dalla funzione deve contenere 15 -2 7 3.

Soluzione Esercizio 40

void InvFile(char *nameF, char *newF)
{
    FILE *f = fopen(nameF, "r");                //Apri il file in lettura
    int n, lenF = 0;
    while (fscanf(f, "%d", &n) == 1) lenF++;    //Conta gli interi nel file
    int k = 0, v[lenF];  //Vettore a dimensione variabile atto a contenere gli interi del file
    rewind(f);           //Riporta il cursore del file all'inizio
    while (
fscanf(f, "%d", &(v[k])) == 1) k++;  //Copia gli interi del file nel vettore v
    fclose(f);
    FILE *nF = fopen(newF, "w");                //Crea ed apri il nuovo file
    for (k = lenF - 1 ; k >= 0 ; k--)           //Scrivi nel nuovo file gli interi in
        fprintf(nF, "%d ", v[k]);               //ordine inverso.
    fclose(nF);
}


Esercizio 41

Scrivere una funzione che prese in input due stringhe fCompact e fMatrix  legge la matrice di interi memorizzata
in formato compatto nel file fCompact e crea un nuovo file di nome fMatrix in cui scrive la stessa matrice in
formato standard. Nel formato compatto la matrice è memorizzata in modo tale che i primi due interi sono il numero di
righe e il numero di colonne della matrice e poi per ogni riga vi è un intero che indica quanti elementi della riga sono diversi
da zero e poi per ognuno di questi elementi il numero di colonna e il valore. Nel formato standard la matrice è memorizzata
in modo tale che i primi due interi sono il numero di righe e il numero di colonne della matrice e poi seguono gli elementi
della matrice disposti per righe (separati da spazi). Entrambi i file sono di tipo testo. Il prototipo della funzione è
                                    void Conv(char *fCompact, char *fMatrix).
Ad esempio se il contenuto del file fCompact è    3 4 2 1 12 3 -2 0 1 4 25   allora la matrice ha 3 righe
e 4 colonne. La prima riga ha due elementi diversi da zero:  quello nella colonna 1 di valore 12 e quello nella
colonna 3 di valore -2.  La seconda riga non ha elementi diversi da zero e la terza riga ne ha uno solo: quello nella
colonna 4 di valore 25. La funzione deve creare un file contenente 3 4 12 0 -2 0 0 0 0 0 0 0 0 25.

Soluzione Esercizio 41

void Conv(char *fCompact, char *fMatrix)
{
    FILE *fC, *fM;
    int r, nr, nc;
    fC = fopen(fCompact, "r");       /* Apri il file in formato compatto in lettura */
    fscanf(fC, "%d %d", &nr, &nc);   /* Leggi il numero di righe e di colonne */
    fM = fopen(fMatrix, "w");        /* Crea ed apri il nuovo file */
    fprintf(fM, "%d %d", nc, nr);    /* Scrivi il numero di righe e di colonne */
    for (r = 1 ; r <= nr ; r++) {    /* Per ogni riga . . . */
        int n, c, ec, ev;
        fscanf(fC, "%d", &n);            /* Leggi il numero di elementi diversi da zero */
        ec = 0;                          /* Colonna del prossimo elemento diverso da */
                                         /* zero (inizializzata a zero).             */

        for (c = 1 ; c <= nc ; c++) {    /* Per ogni colonna . . . */
            if (c > ec && n > 0) {              /* Leggi, se c'e', il prossimo elemento */
                fscanf(fC, "%d %d", &ec, &ev);  /* diverso da zero.                     */
                n--;
            }
            fprintf(fM, " %d", (c == ec ? ev : 0));  /* Scrivi il valore dell'elemento in */
        }                                            /* in posizione (r, c).              */
     }
    fclose(fC);
    fclose(fM);
}


Esercizio 42

Scrivere una funzione che presa in input una lista di interi, modifica la lista in modo tale che i primi n/2 (parte intera inferiore)
elementi, dove n è il numero di elementi della lista, vengono posti in fondo alla lista e restituisce la lista così modificata.
Il tipo degli elementi della lista è così definito:
typedef struct Rec {
    int         info;
    struct Rec *next;
} Rec;
Il prototipo della funzione è  Rec *Sposta(Rec *L). Ad esempio se la lista è
          1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7
 
allora la funzione deve restituire la lista  4 -> 5 -> 6 -> 7 -> 1 -> 2 -> 3.

Soluzione Esercizio 42

Rec *Sposta(Rec *L)
{
    if (L != NULL && L->next != NULL) { 
        int n = 1;                    /* Calcola in n la lunghezza della lista e */
        Rec *p = L;                   /* in p il puntatore all'ultimo elemento.  */
        while (p->next != NULL) {
            p = p->next;
            n++;
        }
        p->next = L;                  /* Accoda i primi elementi */
        p = L;                                     /* Calcola in p il puntatore allo */
        for (n = n/2 ; n > 1 ; n--)
p = p->next;   /* ultimo elemento dei primi n/2. */
        L = p->next;                  /* Sposta i restanti elementi in testa alla lista */
        p->next = NULL;               /* Chiudi la lista */
   }
   return L;
}


Esercizio 43

Scrivere una funzione che preso in input un intero n crea e restituisce una matrice nxn di interi (long) i cui elementi hanno
i valori 1,2,3,. . .,n2. Naturalmente, la matrice deve essere allocata dinamicamente. Il prototipo della funzione è
long **Matrice(short n). Ad esempio, se n = 4, la funzione deve creare la matrice:
1  2  3   4
5  6  7   8
9  10 11  12
13 14 15  16
.

Soluzione Esercizio 43

long **Matrice(short n)
{
    long **M, r, c, k;
    M = malloc(n*sizeof(long *));       /* Alloca il vettore delle righe */
    k = 1;
    for (r = 0 ; r < n ; r++) {         /* Per ogni riga... */ 
        M[r] = malloc(n*sizeof(long));    /* Alloca l'r-esima riga */
        for (c = 0 ; c < n ; c++) {       /* Assegna i valori agli elementi */
            M[r][c] = k;                  /* della r-esima riga.            */
            k++;
        }
    }
    return M;
}


Esercizio 44

Scrivere una funzione, con prototipo long *IndMax(const int V[], long n, long *nMax),  che preso in input un
vettore V di dimensione n, ritorna un vettore di long allocato dinamicamente contenente gli indici degli elementi di V che hanno
valore uguale al massimo intero presente in V e in *nMax restituisce il numero di tali elementi. Il vettore ritornato non deve usare più
memoria di quella strettamente necessaria. Se V = [-1, 2, 7, -5, 7] allora la funzione ritorna il vettore [2, 4] e in
*nMax restituisce 2.

Soluzione Esercizio 44

long *IndMax(const int V[], long n, long *nMax)
{
    long *ind = NULL;            //Inizializza i valori di ind e *nMax cosicche'
    *nMax = 0;                   //se n <= 0 ritorna NULL e in *nMax restituisce 0.
    if (n > 0) {                 
        long i, k;
       
int max = V[0]; 
        for (i = 1 ; i < n ; i++)             //Calcola il valore massimo di V
            if (V[i] > max) max = V[i];
        for (i = 0 ; i < n ; i++)             //Calcola il numero di elementi di V
            if (V[i] == max) (*nMax)++;       //con valore massimo.
        ind = malloc((*nMax)*sizeof(long));   //Alloca il nuovo vettore
        for (k = 0, i = 0 ; i < n ; i++)      //Scrivi nel nuovo vettore gli indici
            if (V[i] == max) ind[k++] = i;    //degli elementi con valore massimo.
    }
    return ind;
}


Esercizio 45

Si consideri il seguente tipo:
typedef struct Estr {
    char *       s;         //puntatore ad una stringa allocata dinamicamente
    struct Estr *next;
}Estr, *Lstr;

Scrivere una funzione, con prototipo Lstr MoveStr(Lstr L, const char *str), che sposta ogni elemento della lista L che ha
la stringa del campo s uguale alla stringa str in coda alla lista e ritorna il puntatore alla lista così modificata. L'ordine degli altri elementi non
deve essere cambiato. Se str = "last"  e  L = "last" -> "first" -> "just" -> "first" -> "last" -> "mah"  
allora la funzione modifica la lista così   "first" -> "just" -> "first" -> "mah" -> "last" -> "last".

Soluzione Esercizio 45

Lstr MoveStr(Lstr L, const char *str)
{
    Lstr *cur = &L;      //Indirizzo della locazione che contiene il puntatore
                         //
all'elemento corrente.
    Lstr mL = NULL;      //Lista degli elementi che devono essere spostati in coda
    while (*cur != NULL) {
        if (strcmp((*cur)->s, str) == 0) {
            Estr *aux = *cur;     //Salva il puntatore all'elemento che deve essere spostato
            *cur = (*cur)->next;  //Togli l'elemento dalla lista L
            aux->next = mL;       //Aggiungi l'elemento in testa alla lista degli elementi
            mL = aux;             //che dovranno essere posti in coda.
        } else cur = &((*cur)->next);
    }
    *cur = mL;           //Aggiungi gli elementi tolti in coda alla lista L
    return L;
}


Esercizio 46

Scrivere una funzione, con prototipo long Inv(char *fName, char *invF), che prende in input il nome fName di un file
di tipo testo contenente una sequenza di parole separate da spazi, crea un nuovo file di tipo testo il cui nome è nella stringa invF, vi scrive
le stesse parole del primo file ma in ordine inverso e ritorna la lunghezza della parola più lunga. Le parole sono sequenze di caratteri diversi
dal carattere spazio. Si assume che tra una parola e quella successiva c'è un solo carattere spazio. Non è dato alcun limite sulla lunghezza
delle parole. Ad esempio se il contenuto del file fName è
rosso Verde1234 08754franconi_56
allora la funzione scrive nel nuovo file
08754franconi_56 Verde1234 rosso
e ritorna 16.

Soluzione Esercizio 46

/* Funzione ausiliaria che legge la k-esima parola dal file f e la copia nella stringa
   str. La funzione assume che il blocco di memoria puntato da str sia abbastanza
   grande da contenere una qualsiasi parola del file.                                  */

void ReadKthWord(FILE *f, long k, char *str)
{
    rewind(f);
    for ( ; k > 0 ; k--) fscanf(f, "%s", str);  
}

long Inv(char *fName, char *invF)
{
    FILE *f = fopen(fName, "r");            //Apri il file in lettura
    long l = 0, maxWord = 0, nWords = 0;
    int c;
    while ((c = fgetc(f)) != EOF) {            //Leggi i caratteri del file e se e' letto
        if (c == ' ') {                        //uno spazio allora una parola e' terminata
            if (l > maxWord) maxWord = l;      //e quindi aggiorna la massima lunghezza e il
            nWords++;                          //numero di parole finora lette e
            l = 0;                             //riporta a 0 il contatore di caratteri.
        } else l++;
    }
    if (l > 0) {                               //Per l'ultima parola del file aggiorna la
       
if (l > maxWord) maxWord = l;          //massima lunghezza e il numero di parole.
        nWords++;
    }                               
        //Dichiara un vettore abbastanza grande da
    char str[maxWord + 1];                   //contenere una qualsiasi parola del file.    
   
FILE *iF = fopen(invF, "w+");            //Crea ed apri il nuovo file
    for ( ; nWords > 0 ; nWords--) {         //Leggi le parole del file in ordine inverso
        ReadKthWord(f, nWords, str);
        fprintf(iF, "%s", str);              //Aggiungi la parola letta al nuovo file
        if (nWords > 1) fprintf(iF, " ");    //Se non e' l'ultima parola scrivi uno spazio
    }
    fclose(f);
    fclose(iF);
    return maxWord;
}


Esercizio 47

Scrivere una funzione che preso in input una stringa e un intero positivo k crea una lista i cui elementi contengono i segmenti
consecutivi di lunghezza k della stringa. Il tipo degli elementi della lista è così definito:
typedef struct
ElemS {
   
    char
*        seg;    /* campo che contiene un segmento (come stringa C) */

    struct
ElemS *next;

} ElemS, *ListaS;

Il prototipo della funzione è ListaS Segment(const char *str, unsigned k). Chiaramente sia gli elementi
della lista che i campi seg di tali elementi devono essere allocati dinamicamente. Ad esempio se la stringa di input è
"Stringa di esempio"
   e   k = 4    allora la funzione deve restituire la lista:
               "Stri" -> "nga " -> "di e" -> "semp" -> "io"
.

Soluzione Esercizio 47

ListaS Segment(const char *str, unsigned k)
{                                   /* La variabile ultimo mantiene l'indirizzo   */
    ListaS L = NULL, *ultimo = &L;  /* della locazione che conterra' il puntatore */
    int i = 0;                      /* all'ultimo elemento che sara' aggiunto.    */
    while (str[i] != '\0') {
        ElemS *nuovo = malloc(sizeof(ElemS));     /* Alloca un nuovo elemento */
        int j, lung = 0;                                   /* Calcola la lunghezza */
        while (str[i + lung] != '\0' && lung < k) lung++;  /* del segmento.        */
        nuovo->seg = malloc((lung + 1)*sizeof(char));      /* Alloca il campo seg e */
        for (j = 0 ; j < lung ; j++)                       /* copiaci il segmento.  */
            nuovo->seg[j] = str[i + j];
        nuovo->seg[lung] = '\0';
        nuovo->next = NULL;                    /* Accoda il nuovo elemento e    */
        *ultimo = nuovo;                       /* aggiorna la variabile ultimo. */
        ultimo = &(nuovo->next);
        i += lung;
    }
    return L;
}

Esercizio 48

Scrivere una funzione che prese in input due stringhe fMatrice e fTrasp, crea un file di nome fTrasp e vi scrive
la trasposta della matrice di interi memorizzata nel file di nome fMatrice. La matrice è memorizzata in modo tale che
i primi due interi sono il numero di righe e il numero di colonne della matrice e poi seguono gli elementi della matrice
disposti per righe (separati da spazi). Entrambi i file sono di tipo testo. Il prototipo della funzione è
                                    void Trasp(char *fMatrice, char *fTrasp).
Si ricorda che la trasposta di una matrice è la matrice che si ottiene scambiando le righe con le colonne della matrice originale.
Ad esempio se il contenuto del file fMatrice è
             2 3 13 8 -4 34 17 104
allora la matrice ha 2 righe, 3 colonne, la prima riga è 13 8 -4 e la seconda riga è  34 17 104. La funzione deve
creare un file contenente  3 2 13 34 8 17 -4 104.

Soluzione Esercizio 48

/* Funzione ausiliaria che ritorna l'intero che si trova nella riga r e colonna c *
 * della matrice memorizzata nel file f.                                          */

int LeggiElem(
FILE *f, int r, int c)
{
   
int nc, n, e;
    rewind(f);               /* Riporta il file all'inizio */
    fscanf(f, "%d", &n);     /* Leggi il numero di righe (che non sara' usato) */
    fscanf(f, "%d", &nc);    /* Leggi il numero di colonne */
    n = r*nc + c;            /* Numero di elementi che precedono quello da leggere */
    while (n > 0)
{             /* Scorri gli elementi che precedono l'elemento */
        fscanf
(f, "%d", &e);

        n--;
    }

    fscanf(f, "%d", &e);        /* Leggi l'elemento */
    return e;
}

void Trasp(char *fMatrice, char *fTrasp)
{
    FILE *fM, *fT;
    int r, c, nr, nc;
    fM = fopen(fMatrice, "r");       /* Apri il file matrice */
    fscanf(fM, "%d %d", &nr, &nc);   /* Leggi il numero di righe e di colonne */
    fT = fopen(fTrasp, "w");         /* Crea ed apri il file fTrasp */
    fprintf(fT, "%d %d", nc, nr);    /* Scrivi il numero di righe e di colonne */
    for (r = 0 ; r < nc ; r++)       /* Scrivi la matrice trasposta nel file fTrasp */
      for (c = 0 ; c < nr ; c++)
          fprintf(fT, " %d", LeggiElem(fM, c, r)); 
    fclose(fM);
    fclose(fT);
}

Esercizio 49

Si consideri il tipo Simbol così definito:
typedef struct Simbol{
    char           s;
    struct Simbol *next;
} Simbol, *SimbolSeq;
Scrivere una funzione che presa in input una lista i cui elementi sono di tipo Simbol elimina dalla lista, se esiste, la prima sotto lista che
inizia con il simbolo '(' termina con il simbolo ')' e non contiene altri simboli '(',  ')' e ritorna la lista così modificata. La memoria
degli elementi eliminati deve essere rilasciata. Il prototipo della funzione è SimbolSeq Riduci(SimbolSeq). Ad esempio se la lista
di input è x -> ( -> y -> + -> ( -> z -> x -> ) -> w -> ) allora la funzione modifica la lista così
x -> ( -> y -> + -> w -> ).

Soluzione Esercizio 49

SimbolSeq Riduci(SimbolSeq ss)
{
    if (ss != NULL && ss->next != NULL) {
        SimbolSeq psL = &ss;        /* Puntatore alla locazione che conterra' l'indirizzo *
                                     * del primo elemento della sotto lista da eliminare. */
        while (*psL != NULL && (*psL)->s != '(')    /* Cerca il primo elemento che */
            psL = &((*psL)->next);                  /* contiene '('.               */
        if (*psL != NULL) {                                       /* Se esiste allora     */
            SimbolSeq last = *psL;                                /* cerca la sotto lista */
            while (last->next != NULL && last->next->s != ')') {  /* da eliminare.        */
                if (last->next->s == '(')           /* Se trovi '(' allora modifica */
                    psL = &(last->next);            /* l'inizio della sotto lista.  */
                last = last->next;
            }
            if (last->next != NULL) {               /* Se la sotto lista esiste */
                while (1) {                         /* eliminala.               */
                    char s = last->s;
                    last = *psL;
                    *psL = last->next;
                    free(last);
                    if (s == ')') break;
                }
            }
        }
    }
    return ss;
}



 

Ritorna alla pagina del corso