# Esercitazione 12: Liste 2
# Correzione Midterm (TDE 13.11.2024)
Scrivere un programma per analizzare i voti conseguiti dagli studenti di una classe del liceo durante tutte le verifiche dell'anno. Le informazioni sono registrate in un vettore di un tipo strutturato, dove ciascun elemento rappresenta uno studente e contiene:
- Un campo "cognome" di tipo stringa con il cognome dello studente.
- Un intero "n" per indicare il numero di verifiche sostenute dallo studente.
- Un array di interi "voti" che contiene i voti dello studente nelle verifiche (le caselle del vettore sono riempite dalla zero e se uno studente ha svolto meno verifiche degli altri le caselle dopo le prime n non hanno contenuto significativo)
1) Scrivere una funzione analizzaStudenti che prende in ingresso un array definito come sopra (ed eventuali variabili aggiuntive) e restituisce sia:
- media_massima: il valore della media per lo studente con la media più alta nel vettore,
- media_totale: la media delle medie di tutti gli studenti nel vettore.
2) Scrivere una funzione studentiBravi che prende in ingresso un array di studenti (ed eventuali variabili aggiuntive) e riporta al chiamante una stringa contenente i cognomi (separati da spazi) di tutti gli studenti con media superiore a media_totale.
Si invochino entrambe le funzioni nel main e si esegua la stampa dei valori calcolati dalle funzioni nel main, non nelle funzioni.
Si parta dal file: https://www.dropbox.com/scl/fi/zzhg7muve4659k13d4sh8/testo.c?rlkey=4kprwk1es8wqs2gmj96z0cswi&dl=0
Col file fornito il programma dovrà stampare
- Media massima di uno studente: 8.33
- Media totale dei voti: 6.87
- Studenti con media superiore alla media totale: Boracchi Campi Conti
```c=
#include <stdio.h>
#include <string.h>
#define NUM_STUDENTI 5
#define NUM_VERIFICHE 5
typedef struct {
char cognome[50];
int voti[NUM_VERIFICHE];
int n; // numero di verifiche effettivo
} Studente;
void analizzaStudenti(Studente[], int, float*, float*);
void studentiBravi(Studente[], int, float, char[]);
int main() {
// dichiarazione ed inizializzazione classe
Studente classe[NUM_STUDENTI] = {
{"Boracchi", {7, 8, 9, 6}, 4},
{"Campi", {8, 8, 8, 8, 8}, 5},
{"Peretti", {4, 6, 7, 5}, 4},
{"Conti", {9, 8, 8}, 3},
{"Valoriani", {4, 6, 7, 3}, 4}
};
// Calcolo medie
float media_totale, media_massima;
analizzaStudenti(classe, NUM_STUDENTI, &media_totale, &media_massima);
// Stampa delle medie
printf("Media massima di uno studente: %.2f\n", media_massima);
printf("Media totale dei voti: %.2f\n", media_totale);
// Studenti con media superiore
char res[5 * 50] = ""; // Inizializzo a stringa vuota
studentiBravi(classe, NUM_STUDENTI, media_totale, res);
// Stampa degli studenti bravi
printf("Studenti con media superiore alla media totale: %s\n", res);
return 0;
}
void analizzaStudenti(Studente classe[], int dim_classe, float* media_totale, float* media_massima) {
*media_totale = 0;
*media_massima = 0;
float tmp_media;
// Ciclo sulla classe
for (int i = 0; i < dim_classe; i++) {
// Calcolo la media dello studente in questione
tmp_media = 0;
for (int j = 0; j < classe[i].n; j++) {
tmp_media += classe[i].voti[j];
}
tmp_media /= classe[i].n;
// Controllo se è la media massima
if (*media_massima < tmp_media) {
*media_massima = tmp_media;
}
// Aggiorno la media totale
*media_totale += tmp_media;
}
// Calcolo la media totale
*media_totale /= dim_classe;
}
void studentiBravi(Studente classe[], int dim_classe, float media_totale, char res[]) {
// Inizializzo la stringa per il risultato
strcpy(res, "");
float tmp_media;
// Ciclo sulla classe per calcolare le medie
for (int i = 0; i < dim_classe; i++) {
tmp_media = 0;
for (int j = 0; j < classe[i].n; j++) {
tmp_media += classe[i].voti[j];
}
tmp_media /= classe[i].n;
// Se la media dello studente è > media_totale, aggiorno la stringa
if (tmp_media > media_totale) {
strcat(res, classe[i].cognome);
strcat(res, " ");
}
}
// Tolgo l'ultimo spazio, se necessario
if (strlen(res) > 0) {
res[strlen(res) - 1] = '\0';
}
}
```
---
# Inserisci in Coda + (Liste di Interi)
Scrivere una variante della funzione inserisci in coda per liste di interi che riesca ad effettuare l’inserimento in coda in tempo costante.
Vale a dire: ogni volta che si vuole inserire un nodo in coda non si deve scorrere tutta la lista.
**Nota**: la soluzione va integrata con il codice dell’esercizio “Operazioni Base su Liste di Interi”.
```c=
// Prototipo
pNode inserisciCodaPlus(pNode, pNode *, int);
// Funzione
pNode inserisciCodaPlus(pNode head, pNode * tail, int new_value){
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if(new == NULL){
printf("Errore di Allocazione.\n");
return head;
}
// Assegno i campi del nuovo nodo
new->data = new_value;
new->next = NULL;
// Controllo se la lista è vuota
if(head == NULL){
// Assegno il nuovo valore alla coda
*tail = new;
// REstituisco la testa
return new;
}
// Modifico il puntatore a next dell'ultimo nodo
(*tail)->next = new;
// Modifico l'ultimo nodo
*tail = new;
// Restituisco la testa (quella data in input)
return head;
}
```
---
# Selection Sort su Liste (Liste di Interi)
Si scriva una funzione che implementi l’algoritmo Selection Sort per ordinare liste di interi.
La funzione deve ordinare la lista direttamente, non copiarla in un’altra lista.
**Nota**: la soluzione va integrata con il codice dell’esercizio “Operazioni Base su Liste di Interi”.
==**Bonus**: e se volessi fare lo swap tra i nodi invece che scambiare i valori?==
```c=
// Prototipo
pNode selectionSortListe(pNode);
// Funzione
pNode selectionSortListe(pNode head) {
// Controllo lista vuota o con un solo elemento
if (head == NULL || head->next == NULL) {
return head;
}
// Variabili per il ciclo
pNode curr_i = head, curr_j;
int tmp;
// Algoritmo di ordinamento
while (curr_i->next != NULL) {
curr_j = curr_i->next;
while (curr_j != NULL) {
if (curr_i->data > curr_j->data) {
// Scambia i dati tra curr_i e curr_j
tmp = curr_i->data;
curr_i->data = curr_j->data;
curr_j->data = tmp;
}
curr_j = curr_j->next;
}
curr_i = curr_i->next;
}
// Restituisco la testa della lista
return head;
}
```
---
# Bubble Sort su Liste (Liste di Interi)
Si scriva una funzione che implementi l’algoritmo Bubble Sort per ordinare liste di interi.
La funzione deve ordinare la lista direttamente, non copiarla in un’altra lista.
**Nota**: la soluzione va integrata con il codice dell’esercizio “Operazioni Base su Liste di Interi”.
==**Bonus**: e se volessi fare lo swap tra i nodi invece che scambiare i valori?==
```c=
pNode bubbleSortListe(pNode head) {
// Controllo se la lista è vuota o ha un solo elemento
if (head == NULL || head->next == NULL) {
return head; // Già ordinata
}
pNode last_sorted = NULL; // Nodo fino al quale la lista è già ordinata
pNode curr;
int tmp, n_swap;
do {
curr = head;
n_swap = 0;
// Scorro la lista fino al nodo già ordinato
while (curr->next != last_sorted) {
if (curr->data > curr->next->data) {
// Scambia i dati tra curr e curr->next
tmp = curr->data;
curr->data = curr->next->data;
curr->next->data = tmp;
n_swap = 1;
}
curr = curr->next; // Avanza al nodo successivo
}
// Aggiorno il nodo fino al quale la lista è già ordinata
last_sorted = curr;
} while (n_swap); // Continua finché ci sono scambi
return head;
}
```
---
# Inversione di Lista (Liste di Interi o Stringhe)
Si scriva una funzione che prenda in input una lista di stringhe e che inverta la lista senza usarne un’altra d’appoggio.
**Nota**: la soluzione va integrata con il codice dell’esercizio “Operazioni Base su Liste di Interi” oppure “Operazioni Base su Liste di Stringhe”.
```c=
// Prototipo
pNode invertiLista(pNode);
// Funzione
pNode invertiLista(pNode head) {
// Controllo se la lista è vuota o ha un solo elemento
if (head == NULL || head->next == NULL) return head;
// Variabili per tracciare i puntatori
pNode curr = head, prec = NULL, succ;
// Scorro la lista e inverto i collegamenti
while (curr != NULL) {
succ = curr->next; // Salvo il successore
curr->next = prec; // Inversione del link
prec = curr; // Avanzo il precedente
curr = succ; // Avanzo il corrente
}
// Restituisco la nuova testa
return prec;
}
```
---
# Catena di Stringhe CCTSF (Liste di Stringhe) (TDE 19.02.2007 p.t. 1)
Data una lista di stringhe, scrivere una funzione che restituisce 1 se la lista rappresenta una catena di parole CCTSF (i.e., compatibile col telefono senza fili).
Una catena di parole si dice CCTSF se è composta da parole adiacenti simili (ovvero con al più due caratteri diversi).
Si supponga di poter utilizzare una funzione con prototipo **int simili(char * s1, char * s2);** che restituisce 1 se le stringhe s1 e s2 sono simili, zero altrimenti.
## Versione Iterativa
```c=
// Prototipo
int stringheCCTSF(pNode head);
int simili(char * s1, char * s2);
// Funzione
int stringheCCTSF(pNode head){
if(head == NULL || head->next == NULL) return 1;
pNode curr = head;
while(curr->next != NULL){
if(!simili(curr->data, curr->next->data)) return 0;
curr = curr->next;
}
return 1;
}
```
## Versione Ricorsiva
```c=
// Prototipo
int stringheCCTSF(pNode head);
int simili(char * s1, char * s2);
// Funzione
int stringheCCTSF(pNode head){
if(head == NULL || head->next == NULL) return 1;
if(!simili(curr->data, curr->next->data)) return 0;
return stringheCCTSF(head->next);
}
```