Esercizio 24C, lanterne estive (lista concatenata)
In questa pagina 5
Testo (scritto del 09/09/2025, parte 3, in C). La Festa delle Lanterne Estive. Svolgi il seguente esercizio sviluppando le funzioni richieste in C. I prototipi delle funzioni non possono essere modificati. Durante l'esame non è possibile consultare appunti o materiale didattico; è consentito utilizzare la documentazione di Linux da terminale con man.
Una famiglia ha decorato il giardino con lanterne luminose appese lungo un sentiero. Ogni lanterna può essere blu, verde o rossa. La disposizione è rappresentata da una lista concatenata: ogni nodo contiene color, il colore della lanterna (di tipo intero). Il colore è rappresentato con tre bit, ciascuno per un colore: 001 blu, 010 verde, 100 rosso.
Si deve verificare che il numero di lanterne di ciascun colore sia lo stesso. Se non lo è, il programma cambia il colore di tutte le lanterne in questo modo: se il numero di lanterne verdi è dispari le colora tutte di rosso; altrimenti (considera pari lo zero) ripete il colore delle prime tre lanterne per tutte le altre (ad esempio con prime tre lanterne blu, verde, rossa: la quarta sarà blu, la quinta verde, la sesta rossa, e così via).
La lista è fornita già implementata: typedef struct node { int color; struct node *next; } Node;. Funzioni da implementare:
void countColors(Node *node, int *colors)conta le lanterne di ciascun colore:colorsè un array di interi e la funzione lo aggiorna (blu incolors[0], verde incolors[1], rosso incolors[2]); per ogni nodo, secolor & 001incrementacolors[0], secolor & 010incrementacolors[1], secolor & 100incrementacolors[2]; poi richiama se stessa sul nodo successivo;int checkLanternColors(Node *head)restituisceTRUE(non zero) se i tre conteggi sono uguali,FALSEaltrimenti; in questo caso applica le regole sopra (secolors[1]è pari: legge i colori dei primi tre nodi in un arraypatterndi interi inizializzato a e chiamaapplyPattern(head, pattern); altrimenti chiamauniformColor(head, 100));void uniformColor(Node *node, int color)impostacolorsu ogni nodo collegato anode(ricorsiva);void applyPattern(Node *node, int *pattern)imposta il colore di ogni nodo in base al pattern, con indice che scorre (index <- (index + 1) % 3).
Lo pseudocodice non è codice C completo: non considera i tipi, l'uso di puntatori e quindi l'operatore -> al posto di .. Si possono creare struct e funzioni ausiliarie.
Richiami
Liste concatenate in C, ricorsione sulle liste (vedi Liste concatenate in CLista singolarmente concatenata in C con nodo struct e testa passata per riferimento (Nodo **); addHead, addTail ricorsiva e iterativa, pop, inversione in loco, liberazione; coda con puntatori a testa e coda; ricorsione sulle liste; costi.Liste concatenate in C →) e operatori bit a bit (&) su interi (vedi Puntatori, struct e memoria dinamica in CPuntatori e passaggio per riferimento in C, array e aritmetica dei puntatori, stringhe, struct e typedef con l'operatore ->, malloc e free, puntatore a puntatore per modificare una testa; compilazione con Makefile; errori tipici (puntatori pendenti, perdite di memoria, off-by-one).Puntatori, struct e memoria dinamica in C → per puntatori e array).
Soluzione
#define BLU 1 /* 001 */
#define VERDE 2 /* 010 */
#define ROSSO 4 /* 100 */
typedef struct node {
int color;
struct node *next;
} Node;
void countColors(Node *node, int *colors) {
if (node == NULL)
return;
if (node->color & BLU) colors[0]++;
if (node->color & VERDE) colors[1]++;
if (node->color & ROSSO) colors[2]++;
countColors(node->next, colors);
}
void uniformColor(Node *node, int color) {
if (node == NULL)
return;
node->color = color;
uniformColor(node->next, color);
}
void applyPattern(Node *node, int *pattern) {
int index = 0;
for (Node *cur = node; cur != NULL; cur = cur->next) {
cur->color = pattern[index];
index = (index + 1) % 3;
}
}
int checkLanternColors(Node *head) {
int colors[3] = {0, 0, 0};
countColors(head, colors);
if (colors[0] == colors[1] && colors[1] == colors[2])
return 1;
if (colors[1] % 2 == 0) { /* verdi pari (anche zero) */
int pattern[3] = {0, 0, 0};
Node *cur = head;
for (int i = 0; i < 3 && cur != NULL; i++) {
pattern[i] = cur->color;
cur = cur->next;
}
applyPattern(head, pattern);
} else {
uniformColor(head, ROSSO);
}
return 0;
}Punti da notare
100nel testo è binario. In C il letterale100è il numero decimale cento. Il rosso è100in binario, cioè : va scritto4,1 << 2o con una costante (ROSSO). ScrivereuniformColor(head, 100)imposterebbe il valore decimale cento (binario1100100): nei conteggi apparirebbe come rosso (il bit del rosso è acceso), ma non è il valore atteso e un confronto del tipocolor == ROSSOfallirebbe.&e non&&:node->color & VERDEisola il bit; un nodo con colore (011) conta sia come blu sia come verde, perché la rappresentazione a bit permette combinazioni.- Il pattern si legge prima di modificare la lista:
applyPatternriscrive anche i primi tre nodi, quindi i loro colori vanno copiati inpatternprima di chiamarla. - Liste con meno di nodi:
patternè inizializzato a e il ciclo di lettura si ferma aNULL, quindi nessun accesso fuori dalla lista;applyPatternusa solopattern[0..1]. - Lista vuota: i conteggi sono tutti , quindi uguali: restituisce vero senza modificare nulla.
- Complessità: ogni funzione scorre la lista una volta: tempo, stack per le due ricorsive (profondità ).
Prove eseguite
Compilato con gcc -std=c99 -Wall -Wextra e provato su liste costruite a mano:
Lista (valori di color) |
Risultato | Lista dopo |
|---|---|---|
| vero | invariata | |
| (verdi: , dispari) | falso | |
| (verdi: , pari) | falso | |
| lista vuota | vero | — |
| falso | ||
| (blu , verdi , rossi ) | falso |
Errori comuni
uniformColor(head, 100)con100decimale, invece di4.- Usare
&&o==al posto di&nel conteggio:color == VERDEnon vede i colori combinati. - Leggere il pattern dopo aver cominciato a riscrivere i nodi.
- Dimenticare il caso della lista con meno di nodi (deferenziare
NULL). - Prototipi modificati (il testo lo vieta).
Versione ripasso
Testo. Lista concatenata di lanterne (color: bit 001 blu, 010 verde, 100 rosso). Se i tre conteggi non sono uguali: verdi dispari ⇒ tutte rosse; altrimenti si ripetono i colori delle prime tre. Implementare in C countColors(Node*, int*), checkLanternColors(Node*), uniformColor(Node*, int), applyPattern(Node*, int*).
- Costanti (vedi Liste concatenate in CLista singolarmente concatenata in C con nodo struct e testa passata per riferimento (Nodo **); addHead, addTail ricorsiva e iterativa, pop, inversione in loco, liberazione; coda con puntatori a testa e coda; ricorsione sulle liste; costi.Liste concatenate in C →):
BLU = 1,VERDE = 2,ROSSO = 4; il100binario del testo è 4, non cento. - countColors: per nodo
if (color & BLU) colors[0]++ecc., poi ricorsione sunext. - checkLanternColors: conta; tre conteggi uguali ⇒ vero;
colors[1] % 2 == 0⇒ copia i primi colori inpattern(inizializzato a ) eapplyPattern; altrimentiuniformColor(head, ROSSO); restituisce falso. - applyPattern:
index = (index + 1) % 3scorrendo la lista; uniformColor: ricorsiva. - Prove: vero; ; ; . .
- Codice:
if (node->color & BLU) colors[0]++;(ripetuto perVERDE,ROSSO);uniformColor:node->color = color; uniformColor(node->next, color);;applyPattern:cur->color = pattern[index]; index = (index + 1) % 3;. - Controllo finale: vero; : verdi ⇒ tutte rosse; : verdi ⇒ .
- Prove con colori combinati: ha blu , verdi , rossi ⇒ verdi pari ⇒ pattern ⇒ ; lista vuota ⇒ vero senza modifiche; lista di due nodi ⇒
pattern[2] = 0non usato. - Errori:
100decimale;&&/==invece di&; pattern letto dopo la riscrittura; meno di nodi.