## 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, e questo va complementato con l'esercizio 00 della prima parte delle liste (Liste 1)==
```c
// Librerie
#include <stdio.h>
#include <stdlib.h> // !!!
// Dati
typedef struct NodeStruct {
int data;
struct NodeStruct * next;
} Node;
typedef Node * pNode;
pNode inserisciOrdinatoNoRip(pNode, int);
pNode eliminaPos(pNode, int);
pNode eliminaElementiValore(pNode, int);
// altre eventuali funzioni nella scorsa esercitazione
// 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;
}
pNode inserisciOrdinatoNoRip(pNode head, int new_data) {
// Alloco il nuovo nodo
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
new_node->data = new_data;
new_node->next = NULL;
// Controllo se il valore è già presente
pNode curr = head, prec = NULL;
while (curr != NULL && new_data > curr->data) {
prec = curr;
curr = curr->next;
}
// Se il valore è già presente, non inserisco nulla
if (curr != NULL && curr->data == new_data) {
printf("Valore %d già presente, non inserito.\n", new_data);
free(new_node);
return head;
}
// Inserimento in testa (prec == NULL)
if (prec == NULL) {
new_node->next = curr;
head = new_node;
}
// Inserimento tra prec e curr
else {
prec->next = new_node;
new_node->next = curr;
}
return head;
}
pNode eliminaPos(pNode head, int index) {
// Controllo se l'indice è valido
if (index < 0) {
printf("Indice %d non valido\n", index);
return head;
}
// Lista vuota
if (head == NULL) {
printf("Lista vuota, impossibile eliminare.\n");
return head;
}
// Eliminazione della testa
if (index == 0) {
pNode temp = head;
head = head->next;
free(temp);
return head;
}
// Altri casi: scorro la lista
pNode curr = head, prec = NULL;
int pos = 0;
while (curr != NULL && pos < index) {
prec = curr;
curr = curr->next;
pos++;
}
// Controllo se l'indice è fuori dai limiti
if (curr == NULL) {
printf("Indice %d fuori dai limiti.\n", index);
return head;
}
// Rimuovo il nodo corrente
prec->next = curr->next;
free(curr);
return head;
}
pNode eliminaElementiValore(pNode head, int value){
pNode curr = head, prec = NULL, tmp;
// Elimino in testa
while(curr != NULL && curr->data == value){
tmp = curr;
curr = curr->next;
free(tmp);
}
// Aggiorno la testa
head = curr;
// Elimino non in testa
while(curr != NULL){
if(curr->data == value){
tmp = curr;
prec->next = curr->next;
curr = curr->next;
free(tmp);
}
else{
prec = curr;
curr = curr->next;
}
}
return head;
}
```
## Esercizio 00: Operazioni Base su Liste di Stringhe
==Qui trovate l'implementazione delle funzioni base su liste di stringhe. Le soluzioni agli esercizi seguenti fanno riferimento a questo codice.==
```c
// Librerie
#include <stdio.h>
#include <stdlib.h>
#include <string.h> // Per funzioni su stringhe
// Macro
#define LEN 100
// Tipo Stringa
typedef char Stringa[LEN];
// Dati
typedef struct NodeStruct {
Stringa data;
struct NodeStruct *next;
} Node;
typedef Node *pNode;
// Prototipi
void stampaLista(pNode);
pNode inserisciCoda(pNode, Stringa);
pNode inserisciTesta(pNode, Stringa);
pNode inserisciPos(pNode, Stringa, int);
pNode inserisciOrdinatoNoRip(pNode, Stringa);
int listaLen(pNode);
int cercaElemento(pNode, Stringa);
void eliminaLista(pNode);
pNode eliminaPos(pNode, int);
pNode eliminaElementiValore(pNode, Stringa);
// Main
int main() {
pNode head = NULL; // Inizializzazione della lista
Stringa s;
printf("Creazione lista con inserimento in testa:\n");
strcpy(s, "Ciao");
head = inserisciTesta(head, s);
strcpy(s, "Mondo");
head = inserisciTesta(head, s);
strcpy(s, "Test");
head = inserisciTesta(head, s);
stampaLista(head);
printf("\nInserimento in coda:\n");
strcpy(s, "Esempio");
head = inserisciCoda(head, s);
stampaLista(head);
printf("\nInserimento ordinato senza duplicati:\n");
strcpy(s, "Alpha");
head = inserisciOrdinatoNoRip(head, s);
strcpy(s, "Test"); // Tentativo duplicato
head = inserisciOrdinatoNoRip(head, s);
stampaLista(head);
printf("\nRicerca di un elemento nella lista:\n");
strcpy(s, "Test");
if (cercaElemento(head, s)) {
printf("Elemento '%s' trovato nella lista.\n", s);
} else {
printf("Elemento '%s' non trovato.\n", s);
}
printf("\nEliminazione del nodo in posizione 1:\n");
head = eliminaPos(head, 1); // Elimina il nodo alla posizione 1
stampaLista(head);
printf("\nEliminazione di tutti i nodi con valore 'Alpha':\n");
strcpy(s, "Alpha");
head = eliminaElementiValore(head, s); // Elimina tutti i nodi con valore 'Alpha'
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;
while (curr != NULL) {
printf("%s -> ", curr->data);
curr = curr->next;
}
printf("NULL\n");
}
}
pNode inserisciCoda(pNode head, Stringa new_data) {
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
strcpy(new_node->data, new_data);
new_node->next = NULL;
if (head == NULL) return new_node;
pNode curr = head;
while (curr->next != NULL) curr = curr->next;
curr->next = new_node;
return head;
}
pNode inserisciTesta(pNode head, Stringa new_data) {
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
strcpy(new_node->data, new_data);
new_node->next = head;
return new_node;
}
pNode inserisciPos(pNode head, Stringa new_data, int index) {
if (index < 0) {
printf("Indice %d non valido\n", index);
return head;
}
if (index == 0) return inserisciTesta(head, new_data);
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
strcpy(new_node->data, new_data);
int pos = 0;
pNode curr = head, prec = NULL;
while (curr != NULL && pos < index) {
prec = curr;
curr = curr->next;
pos++;
}
if (pos != index) {
printf("Indice %d fuori dai limiti.\n", index);
free(new_node);
return head;
}
prec->next = new_node;
new_node->next = curr;
return head;
}
pNode inserisciOrdinatoNoRip(pNode head, Stringa new_data) {
pNode curr = head, prec = NULL;
while (curr != NULL && strcmp(new_data, curr->data) > 0) {
prec = curr;
curr = curr->next;
}
if (curr != NULL && strcmp(new_data, curr->data) == 0) {
printf("Valore '%s' già presente, non inserito.\n", new_data);
return head;
}
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
strcpy(new_node->data, new_data);
new_node->next = curr;
if (prec == NULL) {
return new_node;
} else {
prec->next = new_node;
}
return head;
}
int listaLen(pNode head) {
int len = 0;
pNode curr = head;
while (curr != NULL) {
curr = curr->next;
len++;
}
return len;
}
int cercaElemento(pNode head, Stringa value) {
pNode curr = head;
while (curr != NULL) {
if (strcmp(curr->data, value) == 0) return 1;
curr = curr->next;
}
return 0;
}
void eliminaLista(pNode head) {
pNode curr;
while (head != NULL) {
curr = head;
head = head->next;
free(curr);
}
}
pNode eliminaPos(pNode head, int index) {
if (index < 0) {
printf("Indice %d non valido\n", index);
return head;
}
if (head == NULL) return head;
if (index == 0) {
pNode temp = head;
head = head->next;
free(temp);
return head;
}
pNode curr = head, prec = NULL;
int pos = 0;
while (curr != NULL && pos < index) {
prec = curr;
curr = curr->next;
pos++;
}
if (curr == NULL) return head;
prec->next = curr->next;
free(curr);
return head;
}
pNode eliminaElementiValore(pNode head, Stringa value) {
pNode curr = head, prec = NULL, tmp;
while (curr != NULL && strcmp(curr->data, value) == 0) {
tmp = curr;
curr = curr->next;
free(tmp);
}
head = curr;
while (curr != NULL) {
if (strcmp(curr->data, value) == 0) {
tmp = curr;
prec->next = curr->next;
curr = curr->next;
free(tmp);
} else {
prec = curr;
curr = curr->next;
}
}
return head;
}
```
---
## Esercizio 04: Copia Elemento in una Posizione Specifica (Lista di Interi)
Date due liste di interi e un indice, scrivere una funzione che copia l’elemento della prima lista nella posizione data nella seconda lista, alla stessa posizione.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Interi”
```c
// Prototipo
int copiaPos(pNode, pNode, int);
// Funzione
int copiaPos(pNode h1, pNode h2, int pos) {
// Controllo variabile pos
if (pos < 0) {
printf("Errore: posizione non valida (%d).\n", pos);
return 0;
}
// Variabili di controllo
pNode curr1 = h1, curr2 = h2;
int index = 0;
// Scorro entrambe le liste
while (curr1 != NULL && curr2 != NULL) {
// Controllo se ho raggiunto la posizione desiderata
if (index == pos) {
curr2->n = curr1->n; // Copio il valore
return 1; // Successo
}
// Avanzo nelle liste
curr1 = curr1->next;
curr2 = curr2->next;
index++;
}
// Se arrivo qui, non ho trovato la posizione
printf("Errore: posizione %d fuori dai limiti delle liste.\n", pos);
return 0;
}
```
---
## Esercizio 05: Copia Elementi Pari (Lista di Interi)
Data una lista di interi, scrivere una funzione che cerca nella lista il tutti gli elementi pari e li copia in coda ad una nuova lista.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Interi”
```c
// Prototipo
pNode copiaPari(pNode, pNode);
// Funzione
pNode copiaPari(pNode head1, pNode head2) {
if (head1 == NULL) {
printf("La lista da cui copiare è vuota.\n");
return head2; // Restituisco head2 (potrebbe essere già popolata)
}
pNode curr = head1;
// Copio i numeri pari da head1 a head2
while (curr != NULL) {
if (curr->data % 2 == 0) {
head2 = inserisciCoda(head2, curr->data);
}
curr = curr->next;
}
return head2;
}
```
==**Nota**: in questo modo scorro ogli volta la lista fino all'ultimo elemento, si può fare di meglio? (modificare la funzione inserisciCoda).==
---
## Esercizio 07: Trova Stringa di Lunghezza Minima (Lista di Stringhe)
Data una lista di stringhe, scrivere una funzione che trova e restituisce la stringa di lunghezza minima.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Stringhe”
```c
// Prototipo
void trovaStringaMin(pNode, Stringa, int*);
// Funzione
void trovaStringaMin(pNode head, Stringa res, int *err) {
// Inizializzo errore a 0
*err = 0;
// Caso lista vuota
if (head == NULL) {
printf("Lista vuota.\n");
*err = 1;
strcpy(res, ""); // Resetta `res` a una stringa vuota
return;
}
// Inizializzo `res` con la stringa del primo nodo
strcpy(res, head->data);
// Scorro la lista per trovare la stringa più corta
pNode curr = head->next; // Parto dal secondo nodo
while (curr != NULL) {
if (strlen(res) > strlen(curr->data)) {
strcpy(res, curr->data);
}
curr = curr->next;
}
}
```
---
## Esercizio 08: Copia Stringhe per Prefisso (Liste di Stringhe)
Date due liste di stringhe e una stringa prefisso, scrivere una funzione che trova nella prima lista le stringhe che iniziano per il prefisso dato e copiarle in coda alla seconda lista. Scrivere una funzione separata per controllare che una stringa inizi con un certo suffisso.
**Nota**: la soluzione va integrata con il codice di “Operazione Base su Liste di Stringhe”
```c
// Prototipi
int controllaPrefisso(Stringa, Stringa);
pNode copiaStringhePerPrefisso(pNode, pNode, Stringa);
// Funzioni
int controllaPrefisso(Stringa s, Stringa prefix) {
int len_prefix = strlen(prefix);
// Il prefisso non può essere più lungo della stringa
if (strlen(s) < len_prefix) return 0;
// Confronta caratteri all'inizio
for (int i = 0; i < len_prefix; i++) {
if (s[i] != prefix[i]) return 0;
}
return 1; // È un prefisso
}
pNode copiaStringhePerPrefisso(pNode head1, pNode head2, Stringa prefix) {
if (head1 == NULL) {
printf("Lista vuota.\n");
return head2;
}
pNode curr = head1;
// Scorri la lista `head1` e copia i nodi con il prefisso richiesto
while (curr != NULL) {
if (controllaPrefisso(curr->data, prefix)) {
head2 = inserisciCoda(head2, curr->data);
}
curr = curr->next;
}
return head2;
}
```
==**Nota**: in questo modo scorro ogli volta la lista fino all'ultimo elemento, si può fare di meglio? (modificare la funzione inserisciCoda).==
---
## Esercizio 11: Catena di Stringhe CCTSF (Liste di Stringhe) (TDE 19.02.2007)
**Richiesta 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.
```c=
typedef struct Elem {
char * data;
struct Elem * next;
} Nodo;
typedef Nodo * pNode;
```
### Versione Iterativa (parte 1)
```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 (parte 1)
```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);
}
```
---
## Esercizio 12: Eliminazione di Tutta la Lista
Scrivere una funzione che elimina tutta la lista (sia in modo iterativo che ricorsivo).
==:warning: fare attenzione al valore che assume il puntatore `head` nel main! Come si può fare per farlo modificare in automatico nelle funzioni sottostanti?==
### Iterativo
```c=
void eliminaListaIterativa(pNode head) {
pNode curr;
while (head != NULL) {
curr = head; // Salva il nodo corrente
head = head->next; // Passa al nodo successivo
free(curr); // Libera la memoria del nodo corrente
}
printf("Lista eliminata (iterativa).\n");
}
```
### Ricorsivo
```c=
void eliminaListaRicorsiva(pNode head) {
// Caso base: lista vuota
if (head == NULL) {
printf("Lista eliminata (ricorsiva).\n");
return;
}
// Passo ricorsivo: libera il resto della lista
eliminaListaRicorsiva(head->next);
// Libera il nodo corrente
free(head);
}
```
---
## Esercizio 13: Eliminazione di un Elemento in Posizione $i$
Scrivere una funzione che elimina un singolo elemento nella lista in poisizione $i$ in modo ricorsivo (iterativo visto nelle funzioni base).
```c=
pNode eliminaPosRicorsivo(pNode head, int pos) {
// Caso base: posizione non valida o lista vuota
if (pos < 0) {
printf("Posizione non valida\n");
return head;
}
if (head == NULL) {
printf("Lista vuota\n");
return head;
}
// Caso: eliminazione del primo nodo
if (pos == 0) {
pNode tmp = head;
head = head->next;
free(tmp);
return head;
}
// Passo ricorsivo: procedo alla posizione successiva
head->next = eliminaPosRicorsivo(head->next, pos - 1);
return head;
}
```
---
## Esercizio 14: Eliminazione di Tutti gli Elementi Minori di un Valore Dato
Scrivere una funzione che elimina tutti gli elementi nella lista che sono minori di un certo valore dato (sia in modo iterativo che ricorsivo).
### Iterativo
```c=
pNode eliminaMinoriIterativo(pNode head, int val) {
// Elimino tutti i nodi in testa che soddisfano la condizione
while (head != NULL && head->data < val) {
pNode tmp = head;
head = head->next;
free(tmp);
}
// Se la lista è vuota dopo aver eliminato i nodi in testa
if (head == NULL) return NULL;
// Scorro il resto della lista
pNode curr = head, prec = NULL;
while (curr != NULL) {
if (curr->data < val) {
// Elimina il nodo corrente
pNode tmp = curr;
prec->next = curr->next;
curr = curr->next;
free(tmp);
} else {
// Avanza
prec = curr;
curr = curr->next;
}
}
return head;
}
```
### Ricorsivo
```c=
pNode eliminaMinoriRicorsivo(pNode head, int val) {
// Caso base: lista vuota
if (head == NULL) return NULL;
// Se il nodo corrente deve essere eliminato
if (head->data < val) {
pNode tmp = head;
head = eliminaMinoriRicorsivo(head->next, val); // Passo ricorsivo
free(tmp);
return head;
}
// Nodo corrente non soddisfa la condizione, passo al prossimo
head->next = eliminaMinoriRicorsivo(head->next, val);
return head;
}
```
---
## Esercizio 15: Inserimento Ordinato con Ripetizione
Scrivere una funzione ricorsiva che inserisce un nodo in modo ordinato (anche con ripetizioni).
### Iterativo
```c=
pNode inserisciOrdinatoIterativo(pNode head, int new_data) {
// Alloco il nuovo nodo
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
new_node->data = new_data;
new_node->next = NULL;
// Caso: lista vuota o nuovo nodo da inserire in testa
if (head == NULL || new_data < head->data) {
new_node->next = head;
return new_node;
}
// Trovo la posizione in cui inserire il nuovo nodo
pNode curr = head;
while (curr->next != NULL && curr->next->data < new_data) {
curr = curr->next;
}
// Inserisco il nodo tra curr e curr->next
new_node->next = curr->next;
curr->next = new_node;
return head;
}
```
### Ricorsivo
```c=
pNode inserisciOrdinatoRicorsivo(pNode head, int new_data) {
// Caso base: lista vuota o inserimento in testa
if (head == NULL || new_data < head->data) {
pNode new_node = (pNode)malloc(sizeof(Node));
if (new_node == NULL) {
printf("Errore di allocazione\n");
return head;
}
new_node->data = new_data;
new_node->next = head;
return new_node;
}
// Passo ricorsivo: scorro la lista
head->next = inserisciOrdinatoRicorsivo(head->next, new_data);
return head;
}
```
---
## Esercizio 16: Eliminazione dei Primi $N$ Elementi
Scrivere una funzione che elimini i primi $N$ elementi di una lista (in modo iterativo e ricorsivo).
### Iterativo
```c=
pNode eliminaPrimiElementiIterativo(pNode head, int n) {
// Lista vuota o n non valido
if (head == NULL || n <= 0) return head;
// Scorro ed elimino i primi `n` elementi
pNode curr;
while (head != NULL && n > 0) {
// Salvo il nodo corrente (head)
curr = head;
// Avanzo nella lista
head = head->next;
// Libero il nodo corrente
free(curr);
// Decremento il contatore
n--;
}
// Restituisco la nuova testa della lista
return head;
}
```
### Ricorsivo
```c=
pNode eliminaPrimiElementiRicorsivo(pNode head, int n) {
// Lista vuota o n non valido
if (head == NULL || n <= 0) return head;
// Passo ricorsivo: elimino il primo nodo
pNode new_head = eliminaPrimiElementiRicorsivo(head->next, n - 1);
// Libero il nodo corrente
free(head);
// Restituisco la nuova testa
return new_head;
}
```
---
## Esercizio 17: Eliminazione degli Elementi Duplicati
Scrivere una funzione che elimina gli elementi duplicati da una lista (in modo iterativo e ricorsivo).
**Dai dubbi emersi durante la lezione:**
1. Attenzione: non è come eliminare gli elementi di un dato valore a priori, perché il valore è dato da ogni elemento della lista.
2. Per questo motivo:
a. Scorro la lista fino alla fine in modo ricorsivo
b. Quando ritorno dall'ultimo elemento, ricorsivamente uso quel valore per eliminare (a partire dal nodo successivo) gli elementi con quel valore
:::warning
:warning: Riporto sia le funzioni di interesse, che il codice completo per poterlo provare. Le funzioni di interesse sono commentate di più per poterci focalizzare su quelle
:::
### Iterativo - solo funzione
Idea: Scorro la lista, ed a partire da ogni elemento la scorro di nuovo fino alla fine per vedere se trovo un valore uguale al nodo di partenza.
```c
pNode eliminaDuplicatiIterativo(pNode head) {
// Lista vuota o con un solo elemento
// serve per evitare che l'utente chiami questa funzione su una lista vuota
if (head == NULL || head->next == NULL) return head;
pNode curr = head;
// Scorro la lista esterna
while (curr != NULL) {
pNode runner = curr; // Runner per scorrere il resto della lista
while (runner->next != NULL) {
if (runner->next->data == curr->data) {
// Nodo duplicato trovato
pNode tmp = runner->next;
runner->next = runner->next->next; // Salta il duplicato
free(tmp); // Libera il nodo duplicato
} else {
runner = runner->next; // Avanza
}
}
curr = curr->next; // Avanza nella lista esterna
}
return head;
}
```
### Ricorsivo - solo funzione
Idea:
- Scorro la lista fino alla fine ricorsivamente
- Man mano che torno indietro partendo dal fondo
- Ri-scorro fino alla fine partendo dal prossimo nodo per eliminare gli elementi uguali, aggiornando i `next`
Possibili domande emerse:
- Perché non elimino man mano che vado in fondo?
- è più facile impostare la ricorsione così, provate a fare il contrario e capirete perché
```c=
pNode eliminaElementiUguali(pNode head, int value) {
// elimina in una lista che parte da head, il valore value
// idea: se head->data non è uguale a value, ritorno head (il chiamante ha chiamato per assegnare next).
// altrimenti, ritorno il primo che ha un valore non uguale a quello passato come value
// Caso base: lista vuota
if (head == NULL) return NULL;
// Caso: il nodo corrente contiene `value`
// in questo caso elimino head e trovo la nuova head chiamando ricorsivamente la
// funzione.
if (head->data == value) {
pNode tmp = head; // Salvo il nodo corrente
head = eliminaElementiUguali(head->next, value); // Elimino il resto dei nodi con `value`
free(tmp); // Libero il nodo corrente
// ora NON punto più al nodo che mi è stata passato dal chiamante tramite head, ma tornerò
// il puntatore che mi ritorna la chiamata ricorsiva. Nel caso in cui quella ricorsiva non
// trovasse un elemento uguale subito dopo dopo, ritornerebbe il puntatore al successivo (rispetto alla lista originale).
//
// se c'è una catena di valori uguali, continuo ad entrare in questo if e ritorno un puntatore
// al primo nodo diverso
return head;
}
// Caso: il nodo corrente non contiene `value`, lo tengo buono
// (ovvero tengo la head passata dal chiamante) ed eventualmente aggiorno il next nella
// chiamata successiva.
head->next = eliminaElementiUguali(head->next, value);
return head;
}
pNode eliminaDuplicatiRicorsivo(pNode head) {
// Caso base: lista vuota o con un solo elemento
if (head == NULL || head->next == NULL) return head;
// Elimino ricorsivamente i duplicati dalla lista
// partendo dalla lista che inizia un elemento dopo
head->next = eliminaDuplicatiRicorsivo(head->next);
// Elimino ricorsivamente tutti i nodi uguali al dato di head partendo dall'elemento successivo
// questa viene chiamata partendo dal fondo della lista
head->next = eliminaElementiUguali(head->next, head->data);
return head;
}
```
### Iterativo - codice completo
Qui trovate tutto il codice per eseguire effettivamente le funzioni sopra
:::warning
:warning: il codice delle funzioni qui contiene solo commenti base. Una versione più commentata la trovate [qui](#Iterativo---solo-funzione), nella sezione "Iterativo - solo funzione"
:::
```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 inserisciTesta(pNode, int);
pNode eliminaDuplicatiIterativo(pNode);
int main() {
// inizializzo la lista
int n = 2; // Numero di elementi da eliminare
pNode head = NULL;
head = inserisciTesta(head, 20);
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 703);
// lista: 703 -> 10 -> 10 -> 20
printf("Lista creata:\n");
stampaLista(head);
printf("\n");
// elimino i primi n elementi
head = eliminaDuplicatiIterativo(head);
printf("Lista dopo eliminazione dei duplicati:\n");
stampaLista(head);
return 0;
}
pNode eliminaDuplicatiIterativo(pNode head) {
// Lista vuota o con un solo elemento
if (head == NULL || head->next == NULL) return head;
pNode curr = head;
// Scorro la lista esterna
while (curr != NULL) {
pNode runner = curr; // Runner per scorrere il resto della lista
while (runner->next != NULL) {
if (runner->next->data == curr->data) {
// Nodo duplicato trovato
pNode tmp = runner->next;
runner->next = runner->next->next; // Salta il duplicato
free(tmp); // Libera il nodo duplicato
} else {
runner = runner->next; // Avanza
}
}
curr = curr->next; // Avanza nella lista esterna
}
return head;
}
// 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 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;
}
```
### Ricorsivo - codice completo
Qui trovate tutto il codice per eseguire effettivamente le funzioni sopra.
:::warning
:warning: il codice delle funzioni qui contiene solo commenti base. Una versione più commentata la trovate [qui](#Ricorsivo---solo-funzione), nella sezione "Ricorsivo - solo funzione"
:::
```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 inserisciTesta(pNode, int);
pNode eliminaDuplicatiRicorsivo(pNode);
pNode eliminaElementiUguali(pNode, int);
int main() {
// inizializzo la lista
pNode head = NULL;
head = inserisciTesta(head, 20);
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 10);
head = inserisciTesta(head, 703);
// lista: 703 -> 10 -> 10 -> 20
printf("Lista creata:\n");
stampaLista(head);
printf("\n");
// elimino i duplicati
head = eliminaDuplicatiRicorsivo(head);
printf("Lista dopo eliminazione dei duplicati:\n");
stampaLista(head);
return 0;
}
pNode eliminaElementiUguali(pNode head, int value) {
// Caso base: lista vuota
if (head == NULL) return NULL;
// Caso: il nodo corrente contiene `value`
if (head->data == value) {
pNode tmp = head; // Salvo il nodo corrente
head = eliminaElementiUguali(head->next, value); // Elimino il resto dei nodi con `value`
free(tmp); // Libero il nodo corrente
return head;
}
// Caso: il nodo corrente non contiene `value`
head->next = eliminaElementiUguali(head->next, value);
return head;
}
pNode eliminaDuplicatiRicorsivo(pNode head) {
// Caso base: lista vuota o con un solo elemento
if (head == NULL || head->next == NULL) return head;
// Elimino ricorsivamente i duplicati del resto della lista
head->next = eliminaDuplicatiRicorsivo(head->next);
// Elimino ricorsivamente tutti i nodi uguali al dato di head
head->next = eliminaElementiUguali(head->next, head->data);
return head;
}
// 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 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;
}
```
---