# Liste 1
## Nota sulle soluzioni
Alcune sono da completare con le funzioni elencate nei due "Esercizio 00". Al termine della consegna, è indicata una nota che riporta se l'esercizio è completo o meno. N.B.: Nella prossima esercitazione ci saranno altre operazioni base sulle liste di interi, e di stringhe.
## Esercizio 00: Operazioni Base su Liste di Interi
==Qui trovate l'implementazione delle funzioni base su liste di interi. Le soluzioni agli esercizi seguenti fanno riferimento a questo codice. Sarà da completare con l'esercizio 00 della prossima esercitazione.==
```c
// Librerie
#include <stdio.h>
#include <stdlib.h> // !!!
// Dati
typedef struct NodeStruct {
int data;
struct NodeStruct * next;
} Node;
typedef Node * pNode;
// Prototipi
void stampaLista(pNode);
pNode inserisciCoda(pNode, int);
pNode inserisciTesta(pNode, int);
pNode inserisciPos(pNode, int, int);
int listaLen(pNode);
float listaMedia(pNode, int*);
int cercaElemento(pNode, int);
void eliminaLista(pNode);
// Main
int main() {
pNode head = NULL; // Inizializzazione della lista
int err;
printf("Creazione lista con inserimento in testa:\n");
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 20);
head = inserisciTesta(head, 30);
stampaLista(head);
printf("\nInserimento in coda:\n");
head = inserisciCoda(head, 40);
head = inserisciCoda(head, 50);
stampaLista(head);
printf("\nInserimento in posizione:\n");
head = inserisciPos(head, 25, 2); // Inserisce 25 alla posizione 2
stampaLista(head);
printf("\nInserimento ordinato senza duplicati:\n");
head = inserisciOrdinatoNoRip(head, 35); // Inserisce ordinato
head = inserisciOrdinatoNoRip(head, 10); // Tentativo duplicato
stampaLista(head);
printf("\nCalcolo della lunghezza e media della lista:\n");
int len = listaLen(head);
float media = listaMedia(head, &err);
if (!err) {
printf("Lunghezza: %d, Media: %.2f\n", len, media);
} else {
printf("Errore nel calcolo della media (lista vuota).\n");
}
printf("\nRicerca di un elemento nella lista:\n");
int val = 25;
if (cercaElemento(head, val)) {
printf("Elemento %d trovato nella lista.\n", val);
} else {
printf("Elemento %d non trovato.\n", val);
}
printf("\nEliminazione del nodo in posizione 3:\n");
head = eliminaPos(head, 3); // Elimina il nodo alla posizione 3
stampaLista(head);
printf("\nEliminazione di tutti i nodi con valore 40:\n");
head = eliminaElementiValore(head, 40); // Elimina tutti i nodi con valore 40
stampaLista(head);
printf("\nLiberazione della lista:\n");
eliminaLista(head);
head = NULL;
stampaLista(head); // Verifica che la lista sia vuota
return 0;
}
// Funzioni
void stampaLista(pNode head){
if(head == NULL){
printf("Lista vuota!\n");
}
else{
pNode curr = head;
// Stampo fino al penultimo
while(curr->next != NULL){
printf("%d ->", curr->data);
curr = curr->next;
}
// stampo l'ultimo
printf("%d\n", curr->data);
}
}
pNode inserisciCoda(pNode head, int new_data){
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if(new == NULL){
printf("Errore di allocazione\n");
return head;
}
new->data = new_data;
new->next = NULL;
// Verifico che la lista sia allcocata, altrimenti il nuovo nodo è il primo
if(head == NULL) return new;
// Scorro la lista fino alla fine
pNode curr = head;
while(curr->next != NULL) curr = curr->next;
// curr è l'ultimo elemento, quindi attacco new
curr->next = new;
return head;
}
pNode inserisciTesta(pNode head, int new_data){
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if(new == NULL){
printf("Errore di allocazione\n");
return head;
}
new->data = new_data;
new->next = NULL;
// Bisogna che il nuovo nodo sia la testa e bisogna restituire la testa
new->next = head;
return new;
}
pNode inserisciPos(pNode head, int new_data, int index) {
// Controllo se l'indice è valido
if (index < 0) {
printf("Indice %d non valido\n", index);
return head;
}
// Caso speciale: lista vuota
if (head == NULL) {
if (index == 0) {
return inserisciTesta(head, new_data);
} else {
printf("Lista vuota, impossibile inserire in posizione %d.\n", index);
return head;
}
}
// Caso speciale: inserimento in testa
if (index == 0) {
return inserisciTesta(head, new_data);
}
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if (new == NULL) {
printf("Errore di allocazione\n");
return head;
}
new->data = new_data;
new->next = NULL;
// Altri casi: Scorro la lista per trovare la posizione
int pos = 0;
pNode curr = head, prec = NULL;
while (curr != NULL && pos < index) {
prec = curr;
curr = curr->next;
pos++;
}
// Verifica se l'indice è fuori dai limiti
if (pos != index) {
printf("Indice %d fuori dai limiti.\n", index);
free(new);
return head;
}
// Inserimento del nodo
prec->next = new;
new->next = curr;
return head;
}
int listaLen(pNode head){
int len = 0;
pNode curr = head;
while(curr != NULL){
curr = curr->next;
len ++;
}
return len;
}
int listaMax(pNode head, int* err){
// lista vuota
if(head == NULL){
printf("Lista vuota\n");
*err = 1;
return 0;
}
// altrimenti
*err = 0;
int max = head->data;
pNode curr = head;
while(curr != NULL){
if(max < curr->data) max = curr->data;
curr = curr->next;
}
return max;
}
float listaMedia(pNode head, int* err){
// lista vuota
if(head == NULL){
printf("Lista vuota\n");
*err = 1;
return 0;
}
// altrimenti
*err = 0;
float media = 0;
int len = 0;
pNode curr = head;
while(curr != NULL){
media += curr->data;
curr = curr->next;
len++;
}
return media/len;
}
int cercaElemento(pNode head, int value){
pNode curr = head;
while(curr != NULL){
if(curr->data == value) return 1;
curr = curr->next;
}
return 0;
}
void eliminaLista(pNode head){
pNode curr = head;
while(head != NULL){
curr = head;
head = head->next;
free(curr);
}
}
```
---
## Esercizio 01 Trova Elemento Massimo (Lista di Interi)
Data una lista di interi, scrivere una funzione che trova e restituisce il massimo.
**Nota**: Questa soluzione è completa
```c
// include
#include <stdio.h>
#include <stdlib.h>
// Dati
typedef struct NodeStruct {
int data;
struct NodeStruct *next;
} Node;
typedef Node *pNode;
// Prototipo
void stampaLista(pNode);
int trovaMax(pNode, int*);
pNode inserisciTesta(pNode, int);
int main() {
// errore
int err = 0;
// inizializzo la lista
pNode head = NULL;
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 5);
head = inserisciTesta(head, 20);
head = inserisciTesta(head, 15);
// lista: 15 -> 20 -> 5 -> 10
printf("Lista creata:\n");
stampaLista(head);
printf("\n");
// trovo il massimo
int max = trovaMax(head, &err);
if (!err) {
printf("Il valore massimo nella lista è: %d\n", max);
} else {
printf("La lista è vuota.\n");
}
return 0;
}
// Funzione
int trovaMax(pNode head, int *err) {
// Inizializzo l'errore a 0
*err = 0;
// Gestione della lista vuota
if (head == NULL) {
printf("Errore: Lista vuota.\n");
*err = 1;
return 0; // Valore predefinito in caso di errore
}
// Inizializzo il massimo con il primo elemento
int max = head->data;
pNode curr = head->next;
// Scorro la lista per trovare il massimo
while (curr != NULL) {
if (curr->data > max) {
max = curr->data;
}
curr = curr->next;
}
return max;
}
pNode inserisciTesta(pNode head, int new_data){
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if(new == NULL){
printf("Errore di allocazione\n");
return head;
}
new->data = new_data;
new->next = NULL;
// Bisogna che il nuovo nodo sia la testa e bisogna restituire la testa
new->next = head;
return new;
}
void stampaLista(pNode head){
if(head == NULL){
printf("Lista vuota!\n");
}
else{
pNode curr = head;
// Stampo fino al penultimo
while(curr->next != NULL){
printf("%d ->", curr->data);
curr = curr->next;
}
// stampo l'ultimo
printf("%d\n", curr->data);
}
}
```
---
## Esercizio 02: Somma Multipli di un Numero (Liste di Interi)
Data una lista di interi e un numero n, scrivere una funzione restituisce la somma di tutti i multipli di n nella lista.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Interi”
```c
// Prototipo
int sommaMultipli(pNode, int, int *);
// Funzione
int sommaMultipli(pNode head, int n, int *err) {
*err = 0;
// Controllo lista vuota
if (head == NULL) {
printf("Lista vuota.\n");
*err = 1;
return 0;
}
// Controllo valore di n
if (n == 0) {
printf("Valore non valido.\n");
*err = 1;
return 0;
}
int somma = 0;
pNode curr = head;
// Scorri la lista e somma i multipli di n
while (curr != NULL) {
if (curr->data % n == 0) {
somma += curr->data;
}
curr = curr->next;
}
return somma;
}
```
---
## Esercizio 03: Puntatore Minimo Elemento Pari (Liste di Interi)
Data una lista di interi, scrivere una funzione che cerca il minimo elemento pari e restituisce il puntatore all’area di memoria contenente il nodo associato a quel valore.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Interi”
### Versione 1
```c
// Prototipo
pNode minimoPari(pNode);
// Funzione
pNode minimoPari(pNode head){
if(head == NULL){
printf("Lista vuota.\n");
return NULL;
}
pNode curr = head, pMin = NULL;
int min = 0;
while(curr != NULL){
if(curr->data % 2 == 0){
if(pMin == NULL || min > curr->data){
pMin = curr;
min = curr->data;
}
}
curr = curr->next;
}
return pMin;
}
```
### Versione 2
```c
// Prototipo
pNode minimoPari(pNode);
// Funzione
pNode minimoPari(pNode head) {
if (head == NULL) {
printf("Lista vuota.\n");
return NULL;
}
pNode curr = head;
pNode pMin = NULL;
// Trova il primo nodo con un valore pari
while (curr != NULL) {
if (curr->data % 2 == 0) {
pMin = curr;
break;
}
curr = curr->next;
}
// Se nessun valore pari è trovato
if (pMin == NULL) {
printf("Nessun elemento pari trovato.\n");
return NULL;
}
// Cerca il minimo valore pari
while (curr != NULL) {
if (curr->data % 2 == 0 && curr->data < pMin->data) {
pMin = curr;
}
curr = curr->next;
}
return pMin;
}
```
---
## Esercizio 04: Cerca e Aggiungi (Lista di Interi)
Data una lista di interi e un valore intero, scrivere una funzione che cerca nella lista la PRIMA occorrenza del valore dato e, se lo trova, aggiunge un nodo adiacente a quello trovato, contenente lo stesso valore.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Interi”
==**Nota**: implementare una variante che svole la stessa operazione nella lista per TUTTE le occorrenze del numero in questione.==
```c
// Prototipo
pNode cercaAggiungi(pNode head, int num)
// Funzione
pNode cercaAggiungi(pNode head, int num) {
// Controllo se la lista è vuota
if (head == NULL) {
printf("Lista vuota, impossibile cercare.\n");
return head;
}
// Scorro la lista per cercare il numero
pNode curr = head;
while (curr != NULL) {
if (curr->n == num) {
// Numero trovato, aggiungo una copia come nodo successivo
pNode new = (pNode)malloc(sizeof(Node));
if (new == NULL) {
printf("Errore di allocazione.\n");
return head;
}
new->n = num;
new->next = curr->next;
curr->next = new;
printf("Elemento %d trovato e aggiunto.\n", num);
return head; // Ritorno subito dopo aver aggiunto
}
curr = curr->next;
}
// Se non ho trovato il numero
printf("Elemento %d non trovato.\n", num);
return head;
}
```
---
## Esercizio 05: Trova Massimo Ricorsivo
Come Esercizio $01$, ma ricorsivo. Questo esempio contiene anche delle print per capire meglio cosa succede, ed è già funzionante così (se lo copiate-incollate, va già)
**Nota**: Questa soluzione è completa
```c
// include
#include <stdio.h>
#include <stdlib.h>
// Dati
typedef struct NodeStruct {
int data;
struct NodeStruct *next;
} Node;
typedef Node *pNode;
// Prototipo
void stampaLista(pNode);
pNode trovaMaxRicorsivo(pNode);
pNode inserisciTesta(pNode, int);
int main() {
// inizializzo la lista
pNode head = NULL;
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 5);
head = inserisciTesta(head, 20);
head = inserisciTesta(head, 15);
// lista: 15 -> 20 -> 5 -> 10
// stampo la lista
printf("Lista creata:\n");
stampaLista(head);
printf("\n");
// trovo il massimo
pNode maxNode = trovaMaxRicorsivo(head);
if (maxNode != NULL) {
printf("Il valore massimo nella lista è: %d\n", maxNode->data);
} else {
printf("La lista è vuota.\n");
}
return 0;
}
pNode inserisciTesta(pNode head, int new_data){
// Alloco il nuovo nodo
pNode new = (pNode)malloc(sizeof(Node));
if(new == NULL){
printf("Errore di allocazione\n");
return head;
}
new->data = new_data;
new->next = NULL;
// Bisogna che il nuovo nodo sia la testa e bisogna restituire la testa
new->next = head;
return new;
}
pNode trovaMaxRicorsivo(pNode head) {
// Caso base: lista vuota
if (head == NULL) {
return NULL;
}
printf("[LOG]: chiamata a funzione con:\n");
printf(" head = %d\n", head->data);
// Caso base: un solo elemento
if (head->next == NULL) {
printf(" next: NULL, ritorno %d\n", head->data);
return head;
}
printf(" next: %d, chiamo ricorsivamente\n", head->next->data);
// Chiamata ricorsiva sul resto della lista
// Come fa a trovare il massimo della lista?
// ogni volta che lo chiamo, nella chiamata più interna
// shifto di un elemento oltre. Quando arrivo al fondo,
// risalgo confrontando gli elementi, l'ultimo torna col caso base di
// un solo elemento, risale alla linea dopo la chiamata, e lo confronta
// con l'elemento appena prima, ritorna il massimo tra i due, e così via
pNode maxRest = trovaMaxRicorsivo(head->next);
printf("[LOG]: dopo chiamata ricorsiva, nodo = %d, next = %d\n", head->data, head->next->data);
printf(" ricevuto maxRest = %d\n", maxRest->data);
// Confronto il valore corrente con il massimo del resto della lista
if (maxRest->data > head->data) {
printf(" confrontato %d < %d, ritorno %d\n", head->data, maxRest->data, maxRest->data);
return maxRest;
} else {
printf(" confrontato %d >= %d, ritorno %d\n", head->data, maxRest->data, head->data);
return head;
}
}
void stampaLista(pNode head){
if(head == NULL){
printf("Lista vuota!\n");
}
else{
pNode curr = head;
// Stampo fino al penultimo
while(curr->next != NULL){
printf("%d ->", curr->data);
curr = curr->next;
}
// stampo l'ultimo
printf("%d\n", curr->data);
}
}
```