EuraStudy
Una struttura dati e un modo organizzato di rappresentare e conservare un insieme di dati in memoria, insieme alle operazioni che li manipolano: la scelta della struttura giusta determina la chiarezza e soprattutto l'efficienza degli algoritmi che la usano. In questo appunto si parte dai tipi strutturati statici (array, matrici, stringhe), si passa ai record e ai tipi definiti dall'utente, si studiano le strutture dinamiche lineari (liste concatenate, pile e code), si accenna alle strutture non lineari (alberi e grafi) e si chiude con gli algoritmi fondamentali di ricerca e ordinamento, valutandone il costo con la notazione asintotica.
5 sezioni~25 min di lettura3 competenzeLivello Base 2 · Standard 1 · Approfondimento 2Verificato · 07/2026
livello base
E richiesto saper riconoscere e usare gli array, le pile e le code, descrivendone le operazioni fondamentali e applicando la ricerca lineare/binaria e un ordinamento elementare.
livello avanzato
Nell'opzione Scienze Applicate si chiede di implementare le strutture in un linguaggio reale (anche le liste concatenate), di motivare la scelta della struttura sul problema e di giustificare l'efficienza degli algoritmi con la notazione asintotica O-grande.
Profondità di lettura: Approfondimento
Dimensione del testo: Standard
Array di interi con indici e accesso diretto
Indirizzo di un elemento
In un array, l'indirizzo dell'elemento di indice i si ottiene dall'indirizzo base sommando i volte la dimensione d di un elemento: il calcolo e immediato, quindi l'accesso e a costo costante O(1).
Linearizzazione per righe
In una matrice memorizzata per righe (row-major) con C colonne, l'elemento di riga i e colonna j occupa la posizione lineare i x C + j; gli indici partono da 0.
Una matrice M di interi ha 4 righe e 5 colonne ed e memorizzata per righe (row-major), a partire dall'indirizzo base 1000; ogni intero occupa 4 byte. Determina l'indirizzo in memoria dell'elemento M[2][3] (indici a partire da 0).
Con C = 5 colonne, la posizione lineare e pos = i x C + j = 2 x 5 + 3.
Si moltiplica la posizione per la dimensione d = 4 byte e si somma all'indirizzo base 1000.
13 x 4 = 52, quindi 1000 + 52 = 1052.
Risultato: L'elemento M[2][3] si trova all'indirizzo 1052; l'intera posizione si calcola con poche operazioni aritmetiche, quindi l'accesso e a costo costante O(1).
Errori frequenti
Approfondimento
L'array a dimensione fissa si supera in pratica con l'array dinamico (la «list» di Python, l'«ArrayList» di Java): mantiene la contiguità e l'accesso «O(1)», ma quando si riempie alloca un nuovo blocco più grande — tipicamente di capacità doppia — e vi copia gli elementi. Sebbene la singola copia costi «O(n)», raddoppiando la capacità queste riallocazioni sono rare, e l'analisi ammortizzata mostra che l'aggiunta in coda (append) costa «O(1) ammortizzato»: distribuendo il costo delle riallocazioni su tutte le operazioni, ciascuna pesa in media una costante. La contiguità offre anche un vantaggio pratico spesso decisivo: la località dei dati sfrutta la memoria cache del processore, rendendo la scansione di un array sensibilmente più veloce di quella di una lista concatenata a parità di ordine asintotico. Per le matrici la linearizzazione può essere per righe (row-major, come C e Python) o per colonne (column-major, come Fortran): scandire nell'ordine sbagliato, saltando di riga in riga anziché lungo la riga, fa perdere la località e rallenta il calcolo. Attenzione infine alle stringhe: in molti linguaggi sono immutabili, cioè ogni «modifica» crea una nuova stringa; concatenare in un ciclo n caratteri con «s = s + c» costa allora «O(n²)», perché a ogni passo si ricopia l'intera stringa accumulata — si preferisce raccogliere i pezzi in una lista e unirli una sola volta alla fine.
Ripasso attivo
Dato un array A di n numeri interi, scrivi (in pseudocodice o in un linguaggio a tua scelta) un algoritmo che calcoli la somma e il valore massimo degli elementi con una sola scansione, indicandone il costo asintotico in funzione di n.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) - Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Record Studente: campi eterogenei sotto un unico nome
Dato un array elenco di N record Studente (con i campi cognome, matricola, media), scrivi un algoritmo che calcoli la media aritmetica del campo media su tutti gli studenti e indicane il costo.
Si pone un accumulatore somma a 0.
Per i da 0 a N-1 si somma elenco[i].media all'accumulatore: si accede prima all'elemento per indice, poi al campo per nome.
Al termine si divide la somma per N (con N > 0).
Risultato: L'algoritmo esegue una sola scansione dell'array, quindi ha costo lineare O(N); restituisce la media dei voti se N > 0, altrimenti il valore non e definito.
Errori frequenti
Approfondimento
Il record è il primo passo verso i tipi di dato astratti (Abstract Data Type, ADT), una delle idee più potenti dell'informatica: un ADT descrive un tipo attraverso le operazioni che vi si possono compiere e le loro proprietà (l'interfaccia, il «che cosa»), nascondendone la realizzazione interna (l'implementazione, il «come»). Pila, coda, lista, insieme e dizionario sono tutti ADT: se ne può cambiare l'implementazione — con array oppure con lista concatenata — senza toccare il codice che li usa, purché l'interfaccia resti la stessa. È l'astrazione che rende il software componibile. Il record fornisce i dati; unendovi le operazioni si arriva all'oggetto della programmazione a oggetti, dove l'incapsulamento rende l'astrazione anche protetta da usi scorretti. In memoria i campi di un record occupano celle vicine, eventualmente con qualche byte di riempimento (padding) per rispettare i vincoli di allineamento dell'architettura. Sul piano progettuale, la scelta fra «array di record» (array of structures) e «record di array» (structure of arrays, cioè array paralleli, quelli sconsigliati nei tranelli) non è solo estetica: il primo tiene insieme i dati di ciascuna entità ed è più leggibile e sicuro contro i disallineamenti; il secondo può però favorire elaborazioni che toccano un solo campo di moltissime entità. Riconoscere questo compromesso significa già ragionare da progettista di strutture dati, non da semplice utilizzatore.
Ripasso attivo
Definisci un tipo record Libro con i campi titolo (stringa), anno (intero) e prezzo (reale); dichiara poi un array di 100 libri e scrivi l'istruzione che assegna 19.90 al prezzo del terzo libro del catalogo.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) - Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Pila (LIFO) e coda (FIFO) a confronto con le rispettive operazioni
Lista concatenata: nodi (informazione + puntatore al successivo)
Una pila e inizialmente vuota. Esegui nell'ordine: push(5), push(8), pop, push(3), pop, push(7). Indica l'elemento estratto da ciascun pop e il contenuto finale della pila dalla base alla cima.
La pila diventa, dalla base alla cima: 5, 8. La cima e 8.
Si rimuove la cima: viene estratto 8 (l'ultimo inserito, LIFO). Resta: 5.
Si inserisce 3 in cima. La pila diventa: 5, 3.
Si rimuove la cima: viene estratto 3. Resta: 5.
Si inserisce 7 in cima. La pila diventa: 5, 7.
Risultato: I pop estraggono nell'ordine 8 e poi 3; il contenuto finale della pila, dalla base alla cima, e 5, 7.
Errori frequenti
Approfondimento
La lista concatenata di base ha varianti importanti: la lista doppiamente concatenata aggiunge in ogni nodo un puntatore anche al predecessore, permettendo di scorrere in entrambi i versi e di cancellare un nodo noto in «O(1)» senza risalire la catena; la lista circolare fa puntare l'ultimo nodo al primo, utile per gestioni cicliche (turnazione round-robin). Pila e coda sono ADT implementabili sia su lista sia su array: la pila su array usa un indice «cima» che sale e scende; la coda su array conviene realizzarla come coda circolare (buffer circolare), con due indici «testa» e «coda» che avanzano modulo la capacità, così da riusare le celle liberate senza spostamenti — altrimenti ogni «dequeue» lascerebbe un buco all'inizio. Una variante utile è la deque (coda a doppia entrata), che ammette inserimenti ed estrazioni a entrambi gli estremi. L'applicazione più elegante della pila è la valutazione delle espressioni: un'espressione in notazione postfissa (polacca inversa, RPN) come «3 4 + 5 ·» si valuta scorrendola da sinistra a destra, impilando i numeri e, a ogni operatore, estraendo dalla cima i due operandi, applicando l'operazione e reimpilando il risultato — al termine la pila contiene il valore «(3+4)·5 = 35». È lo stesso identico meccanismo con cui il processore gestisce le chiamate annidate di funzione sulla pila dei record di attivazione, ciò che lega questo argomento alla ricorsione.
Ripasso attivo
Simula su carta una pila inizialmente vuota eseguendo, nell'ordine: push(5), push(8), pop, push(3), pop, push(7); indica per ogni pop l'elemento estratto e mostra il contenuto finale della pila dalla base alla cima.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) - Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Albero binario: radice, nodi interni e foglie
Considera l'albero binario di radice 8 con figli 3 (sinistro) e 10 (destro), dove 3 ha i figli 1 e 6 e 10 ha il solo figlio destro 14. Individua la radice, elenca le foglie e determina l'altezza dell'albero.
La radice e il nodo da cui parte la gerarchia e che non ha genitore: e il nodo 8.
Le foglie sono i nodi senza figli: 1, 6 e 14.
L'altezza e la profondita massima, cioe il numero massimo di archi da radice a foglia: dal nodo 8 al nodo 6 si percorrono 2 archi (8 -> 3 -> 6).
Risultato: La radice e 8, le foglie sono 1, 6 e 14 e l'altezza dell'albero e 2.
Errori frequenti
Approfondimento
Ciò che rende alberi e grafi operativi sono gli algoritmi di visita, che percorrono sistematicamente tutti i nodi. Su un albero binario si distinguono le visite in profondità — anticipata (preordine: radice, sinistro, destro), simmetrica (inordine: sinistro, radice, destro) e posticipata (postordine: sinistro, destro, radice) — e la visita in ampiezza, per livelli. La visita inordine di un albero binario di ricerca (BST), in cui per ogni nodo le chiavi del sottoalbero sinistro sono minori e quelle del destro maggiori, restituisce gli elementi già in ordine crescente. Il BST è potente perché ricerca, inserimento e cancellazione seguono un solo cammino radice-foglia: se l'albero è bilanciato (altezza «≈ log₂ n») queste operazioni costano «O(log n)», ma se degenera in una catena — per esempio inserendo dati già ordinati — l'altezza diventa «n» e il costo ricade a «O(n)»; da qui gli alberi auto-bilancianti (AVL, alberi rosso-neri). Sui grafi le due visite fondamentali sono la ricerca in ampiezza (BFS), che usa una coda e visita i vertici per distanza crescente dalla sorgente, trovando i cammini minimi in numero di archi, e la ricerca in profondità (DFS), che usa una pila (o la ricorsione) e si spinge il più lontano possibile prima di tornare indietro; entrambe toccano ogni vertice e ogni arco una sola volta, con costo «O(V + E)». Su grafi pesati il cammino di costo minimo si trova con l'algoritmo di Dijkstra, che generalizza la BFS servendosi di una coda con priorità.
Ripasso attivo
Disegna un albero binario di radice 8 con i figli 3 (sinistro) e 10 (destro), dove 3 ha come figli 1 e 6 e 10 ha il solo figlio destro 14; individua la radice, le foglie e l'altezza dell'albero.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) - Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM))
Ricerca binaria di 23 su un array ordinato di 10 elementi
Costo ricerca lineare
La ricerca sequenziale nel caso peggiore confronta tutti gli n elementi: il costo cresce linearmente con n.
Costo ricerca binaria
Dimezzando a ogni passo l'intervallo di ricerca, il numero di confronti e dell'ordine del logaritmo in base 2 di n; richiede pero un array ordinato.
Costo ordinamenti elementari
Selection, insertion e bubble sort confrontano nel caso peggiore un numero di coppie proporzionale a n^2: il costo cresce con il quadrato della dimensione.
Crescita del costo: confronto tra O(log n), O(n) e O(n²)
Sull'array ordinato [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] (indici da 0 a 9) applica la ricerca binaria per cercare il valore 23; indica a ogni passo l'elemento centrale confrontato e conta i confronti.
Intervallo [0, 9]: centro all'indice (0+9)/2 = 4, valore 16. Poiche 23 > 16, si scarta la meta sinistra e si prosegue su [5, 9].
Intervallo [5, 9]: centro all'indice (5+9)/2 = 7, valore 56. Poiche 23 < 56, si scarta la meta destra e si prosegue su [5, 6].
Intervallo [5, 6]: centro all'indice (5+6)/2 = 5, valore 23. Coincide con il valore cercato: trovato all'indice 5.
Risultato: Il valore 23 viene trovato all'indice 5 in 3 confronti; coerentemente con il costo O(log n), poiche log2(10) e circa 3.32, sono sufficienti pochi confronti contro i fino a 10 della ricerca lineare.
Errori frequenti
Approfondimento
Vale la pena derivare esattamente i costi, non solo enunciarli. Negli ordinamenti elementari, per sistemare l'i-esimo elemento si eseguono nell'ordine di «i» confronti: il totale è «1 + 2 + ... + (n−1) = n(n−1)/2», che per n grande è dell'ordine di «n²/2», cioè «O(n²)». Il selection sort compie sempre questo numero di confronti; l'insertion sort ne fa altrettanti nel caso peggiore (dati in ordine inverso), ma solo «O(n)» sul quasi-ordinato, che è il suo caso migliore. Gli algoritmi «O(n log n)» applicano invece il paradigma «divide et impera». Il merge sort divide l'array in due metà, le ordina ricorsivamente e le fonde in tempo lineare: il suo costo obbedisce alla ricorrenza «T(n) = 2·T(n/2) + O(n)», che sviluppata produce «log₂ n» livelli, ciascuno di costo complessivo «O(n)», da cui «T(n) = O(n log n)». Il quick sort partiziona attorno a un perno (pivot): in media «O(n log n)», ma «O(n²)» nel caso peggiore, con perno sistematicamente pessimo. Due proprietà distinguono gli ordinamenti in pratica: la stabilità (un ordinamento è stabile se conserva l'ordine relativo di elementi con chiave uguale) e la memoria occupata (il merge sort richiede un array ausiliario, il quick sort ordina «sul posto»). Infine un risultato teorico notevole: nessun algoritmo di ordinamento basato su confronti può scendere sotto «O(n log n)» nel caso peggiore — è un limite inferiore «Ω(n log n)» che si dimostra contando le foglie dell'albero di decisione, poiché con n elementi le permutazioni possibili sono «n!» e servono almeno «log₂(n!) ≈ n log₂ n» confronti per distinguerle.
Ripasso attivo
Sull'array ordinato [2, 5, 8, 12, 16, 23, 38, 56, 72, 91] applica la ricerca binaria per cercare il valore 23: indica a ogni passo l'elemento centrale confrontato, il numero totale di confronti e l'esito.
Richiamo attivo
Ricorda i punti chiave — poi rivela.
Fonti: Indicazioni Nazionali per i Licei (DPR 89/2010, DM 211/2010) - Obiettivi Specifici di Apprendimento (Ministero dell'Istruzione e del Merito (MIM)) · Esame di Stato del secondo ciclo - quadri di riferimento e griglie di valutazione (Ministero dell'Istruzione e del Merito (MIM))
Riferimenti e fonti