Salta al contenuto
Note per Studenti Esercizio 24 · C, lanterne estive (lista concatenata)

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:

  1. void countColors(Node *node, int *colors) conta le lanterne di ciascun colore: colors è un array di 33 interi e la funzione lo aggiorna (blu in colors[0], verde in colors[1], rosso in colors[2]); per ogni nodo, se color & 001 incrementa colors[0], se color & 010 incrementa colors[1], se color & 100 incrementa colors[2]; poi richiama se stessa sul nodo successivo;
  2. int checkLanternColors(Node *head) restituisce TRUE (non zero) se i tre conteggi sono uguali, FALSE altrimenti; in questo caso applica le regole sopra (se colors[1] è pari: legge i colori dei primi tre nodi in un array pattern di 33 interi inizializzato a 00 e chiama applyPattern(head, pattern); altrimenti chiama uniformColor(head, 100));
  3. void uniformColor(Node *node, int color) imposta color su ogni nodo collegato a node (ricorsiva);
  4. void applyPattern(Node *node, int *pattern) imposta il colore di ogni nodo in base al pattern, con indice che scorre 0,1,2,0,1,2,…0, 1, 2, 0, 1, 2, \dots (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; gli operatori bit a bit sono in Tipi, operatori e controllo del flusso in CTipi interi e reali del C con dimensioni e limiti, conversioni implicite e cast, divisione intera, operatori di incremento, logici e bit a bit; if, switch, while, do-while, for, break e continue.Tipi, operatori e controllo del flusso in C →, la rappresentazione binaria in Rappresentazione binaria dei datiBasi 2, 8 e 16; interi senza segno e in complemento a 2 con overflow; virgola mobile IEEE 754 e sue approssimazioni; caratteri ASCII, Unicode e UTF-8.Rappresentazione binaria dei dati → e l'AND logico in Algebra di Boole e porte logicheVariabili booleane, operatori AND, OR, NOT e derivati (NAND, NOR, XOR, XNOR) con tabelle di verità; assiomi e teoremi dell'algebra di Boole, De Morgan; porte logiche e completezza di NAND e NOR; semplificazione algebrica con esempio.Algebra di Boole e porte logiche →). Ogni colore è un bit di una maschera: & con la maschera mm dà un valore non nullo se e solo se il bit corrispondente è acceso.

colore (color) binario & 1 (blu) & 2 (verde) & 4 (rosso)
1 001 ≠0\ne0 0 0
2 010 0 ≠0\ne0 0
4 100 0 0 ≠0\ne0
3 011 ≠0\ne0 ≠0\ne0 0
7 111 ≠0\ne0 ≠0\ne0 ≠0\ne0

Esempio: per la lista 1, 2, 2, 4, 4, 4 countColors dà colors = [1, 2, 3] (un blu, due verdi, tre rossi): non uguali, verdi pari, quindi si applica il pattern 1, 2, 2.

Soluzione

c
#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

Prove eseguite

Compilato con gcc -std=c99 -Wall -Wextra e provato su liste costruite a mano:

Lista (valori di color) Risultato Lista dopo
1,2,41, 2, 4 vero invariata
1,1,2,4,41, 1, 2, 4, 4 (verdi: 11, dispari) falso 4,4,4,4,44, 4, 4, 4, 4
1,2,2,4,4,41, 2, 2, 4, 4, 4 (verdi: 22, pari) falso 1,2,2,1,2,21, 2, 2, 1, 2, 2
lista vuota vero —
1,11, 1 falso 1,11, 1
3,4,7,13, 4, 7, 1 (blu 33, verdi 22, rossi 22) falso 3,4,7,33, 4, 7, 3

Errori comuni

  • uniformColor(head, 100) con 100 decimale, invece di 4.
  • Usare && o == al posto di & nel conteggio: color == VERDE non vede i colori combinati.
  • Leggere il pattern dopo aver cominciato a riscrivere i nodi.
  • Dimenticare il caso della lista con meno di 33 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; il 100 binario del testo è 4, non cento.
  • countColors: per nodo if (color & BLU) colors[0]++ ecc., poi ricorsione su next.
  • checkLanternColors: conta; tre conteggi uguali ⇒ vero; colors[1] % 2 == 0 ⇒ copia i primi ≤3\le 3 colori in pattern (inizializzato a 00) e applyPattern; altrimenti uniformColor(head, ROSSO); restituisce falso.
  • applyPattern: index = (index + 1) % 3 scorrendo la lista; uniformColor: ricorsiva.
  • Prove: [1,2,4][1,2,4] vero; [1,1,2,4,4]→[4,4,4,4,4][1,1,2,4,4] \to [4,4,4,4,4]; [1,2,2,4,4,4]→[1,2,2,1,2,2][1,2,2,4,4,4] \to [1,2,2,1,2,2]; [3,4,7,1]→[3,4,7,3][3,4,7,1] \to [3,4,7,3]. Θ(n)\Theta(n).
  • Codice: if (node->color & BLU) colors[0]++; (ripetuto per VERDE, ROSSO); uniformColor: node->color = color; uniformColor(node->next, color);; applyPattern: cur->color = pattern[index]; index = (index + 1) % 3;.
  • Controllo finale: [1,2,4][1,2,4] vero; [1,1,2,4,4][1,1,2,4,4]: verdi 11 ⇒ tutte rosse; [1,2,2,4,4,4][1,2,2,4,4,4]: verdi 22 ⇒ [1,2,2,1,2,2][1,2,2,1,2,2].
  • Prove con colori combinati: [3,4,7,1][3,4,7,1] ha blu 33, verdi 22, rossi 22 ⇒ verdi pari ⇒ pattern [3,4,7][3,4,7] ⇒ [3,4,7,3][3,4,7,3]; lista vuota ⇒ vero senza modifiche; lista di due nodi ⇒ pattern[2] = 0 non usato.
  • Errori: 100 decimale; &&/== invece di &; pattern letto dopo la riscrittura; meno di 33 nodi.

Teoria collegata