edubun
Italiano

Array

Leggere il milionesimo valore di un array richiede un passo, ma mettere un nuovo valore in testa allo stesso array sposta un milione di valori.

Che cos'è una struttura dati

Un programma che tiene una lista di nomi, una coda di stampe o una tabella di prezzi deve memorizzare quei dati da qualche parte, e deve rispondere a domande su di essi: qual è il terzo elemento, questo nome è nella lista, quale stampa viene dopo? Una struttura dati è un modo di memorizzare i dati insieme ai metodi che rispondono a quelle domande. Il corso 6.006 del MIT separa qui due idee. L'interfaccia dice quali operazioni puoi chiedere, come «prendi l'elemento in posizione i» o «aggiungi un elemento alla fine». La struttura dati è il modo in cui quelle operazioni vengono davvero eseguite in memoria. Dietro una sola interfaccia possono esserci diverse strutture dati, e la loro velocità può essere molto diversa.

Questo corso conta i passi, come il corso di algoritmi, così due strutture si possono confrontare senza cronometro. In questa unità un passo è la lettura o la scrittura di una cella di memoria. Il laboratorio qui sotto li conta per te.

Un blocco di celle, una accanto all'altra

Un array memorizza i suoi valori in un unico blocco di memoria senza interruzioni, ogni valore in una cella della stessa dimensione, numerata da 0. È questa disposizione che rende gli array veloci da leggere. Se il blocco inizia all'indirizzo s e ogni cella occupa c byte, allora a[i] si trova all'indirizzo s + i × c. Il computer lo calcola con una moltiplicazione e un'addizione e salta direttamente lì, che i valga 3 o 3000000. Gli appunti del MIT indicano «get at(i)» e «set at(i, x)» per un array come Θ(1): il costo non cresce con la lunghezza.

La stessa disposizione rende lente le modifiche nel mezzo. Supponi che l'array contenga 7, 3, 9, 4, 6 e che tu voglia 1 in testa. Davanti al 7 non c'è spazio, quindi prima ogni valore deve spostarsi di una cella a destra, partendo dall'ultimo perché niente venga sovrascritto. Sono 5 spostamenti e 1 scrittura: 6 passi. Togliere il valore in testa è il contrario: lo leggi, poi sposti gli altri 4 valori di una cella a sinistra. Inserire in posizione i in un array di n valori costa n − i spostamenti più una scrittura, quindi la testa è il posto peggiore e la fine il migliore.

Caricamento

Trascina per ruotare. Scorri o avvicina le dita per lo zoom.

Un array di cinque valori in un blocco di cinque celle. Muovi il cursore per scegliere i, poi leggi, inserisci o togli. Un push sul blocco pieno copia tutto in un blocco grande il doppio.

Quando il blocco è pieno

Un blocco di memoria ha una dimensione fissa: che cosa succede quando aggiungi un valore e tutte le celle sono occupate? L'array deve chiedere un blocco nuovo e più grande e copiarci ogni valore prima di poter scrivere quello nuovo. Nel laboratorio, un push sul blocco pieno di 5 costa 5 copie e 1 scrittura. Se l'array crescesse di una sola cella ogni volta, ogni push copierebbe tutto, e n push costerebbero circa n²/2 copie.

La soluzione descritta negli appunti del MIT è crescere molto in una volta, per esempio raddoppiando. Partendo da capacità 1, le copie avvengono quando l'array contiene 1, 2, 4, 8 e 16 valori, quindi 17 push costano in tutto 1 + 2 + 4 + 8 + 16 = 31 copie, meno di 2 per push. Un singolo push può ancora essere lento, ma una lunga serie di push ha in media un costo costante per push. Si parla di tempo Θ(1) ammortizzato. Visto da fuori, un array di JavaScript si comporta così: MDN descrive gli array come ridimensionabili, e push e pop lavorano alla fine, che è l'estremità economica.

Operazione su n valoriPassi nel laboratorioCresce come
Leggere o scrivere a[i]1Θ(1)
Inserire in testan spostamenti + 1 scritturaΘ(n)
Togliere la testa1 lettura + (n − 1) spostamentiΘ(n)
Push alla fine (c'è spazio)1Θ(1)
Push alla fine (blocco pieno)n copie + 1 scritturaΘ(n) una volta, Θ(1) ammortizzato
const a = [7, 3, 9, 4, 6];
console.log(a[3]);      // 4, un passo
a.push(8);              // economico: la fine
a.unshift(1);           // ogni valore si sposta di una cella a destra
console.log(a.join(" ")); // 1 7 3 9 4 6 8
ContestoGli array sono più vecchi della maggior parte dei linguaggi di programmazione. Le prime macchine memorizzavano una tabella come parole numerate in memoria, e i programmatori calcolavano a mano l'indirizzo della voce i, proprio come fa la formula qui sopra. Il Fortran, pubblicato da IBM nel 1957, rese gli array parte del linguaggio, così si poteva scrivere A(I) e lasciare al compilatore il calcolo dell'indirizzo. Il Fortran contava da 1. C e JavaScript contano da 0, e questo si adatta alla formula: a[0] sta proprio all'indirizzo di partenza.
InterfacciaL'elenco delle operazioni che puoi chiedere, come get at(i) o insert last(x).
Struttura datiIl modo in cui quelle operazioni vengono eseguite in memoria.
ArrayValori in un blocco di celle uguali; a[i] sta a partenza + i × dimensione della cella.
Array dinamicoUn array che cresce copiandosi in un blocco più grande, di solito il doppio.
Costo ammortizzatoIl costo medio per operazione su una lunga serie, anche se alcune operazioni singole sono lente.
  1. Con il conteggio dei passi di questa unità, quanti passi servono per leggere a[700000] da un array di 1000000 valori?
    • 1
    • 20
    • 700,000
    • 1,000,000
  2. Un array contiene 7, 3, 9, 4, 6 e ha spazio libero. Quanti passi servono per inserire un valore in testa (spostamenti più la scrittura)?
  3. Stesso array di 5 valori: quanti passi servono per togliere il valore in testa (1 lettura più gli spostamenti)?
  4. Inserire in posizione 2 in un array di 5 valori (posizioni da 0 a 4) con spazio libero: quanti passi?
  5. Un array che raddoppia contiene 5 valori e ha capacità 5. Fai il push di un altro valore. Quanti passi richiede quel push?
  6. Dopo quel push, qual è la capacità?
  7. Partendo da capacità 1 e raddoppiando, quante copie fanno in tutto 17 push?
  8. Perché il push su un array che raddoppia si dice Θ(1) ammortizzato?
    • Ogni singolo push richiede esattamente un passo
    • Le copie sono rare, quindi il totale su n push resta sotto una costante per n
    • Il computer copia gratis in sottofondo
    • JavaScript non copia mai gli array
  9. Nei termini del corso 6.006 del MIT, «insert last(x) e delete last()» è
    • una struttura dati
    • un'interfaccia
    • un indirizzo di memoria
    • un metodo di ordinamento
  10. Un array ha lunghezza n e conta da 0. Qual è l'indice del suo ultimo valore?
    • n
    • n − 1
    • n + 1
    • 1
Punti chiave
  • Un'interfaccia elenca le operazioni; una struttura dati è il modo in cui vengono eseguite.
  • Un array tiene i valori in un solo blocco, quindi a[i] richiede un passo.
  • Inserire o togliere vicino alla testa sposta circa n valori.
  • Un array che raddoppia rende il push Θ(1) ammortizzato: 17 push da capacità 1 copiano 31 valori.
Fonti di questo corso (11)
  • MIT OpenCourseWare 6.006 Introduction to Algorithms, Spring 2020 (Demaine, Ku, Solomon)
  • lecture notes 2 Data Structures (interface vs data structure, Sequence and Set interfaces, stack = insert_last/delete_last, queue = insert_last/delete_first, array, linked list, dynamic array, amortized Θ(1) append)
  • 4 Hashing (chaining, pigeonhole collisions, chain length n/m)
  • 6 Binary Trees Part 1 (height, traversal order, O(h) operations) and 7 Binary Trees Part 2: AVL (Adelson-Velsky and Landis, 1962) at ocw.mit.edu/courses/6-006-introduction-to-algorithms-spring-2020/pages/lecture-notes
  • MDN Web Docs, Array.prototype.shift (shifts all values left by 1)
  • Array.prototype.unshift, Map (insertion order, Map vs Object, spec requires sublinear average access) and Set (developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects)
  • ECMAScript 2025 Language Specification (tc39.es/ecma262)
  • 24.1 Map Objects and 24.2 Set Objects ("must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements")
  • Array.prototype.shift steps (Repeat, while k < len: move element k to k - 1)
  • D. E. Knuth, The Art of Computer Programming vol. 3 (2nd ed. 1998) section 6.4 History (H. P. Luhn, IBM internal memorandum January 1953, hashing with chaining; digit pairs added mod 10, 31415926 -> 4548) via gwern.net copy, and IEEE Spectrum, "Hans Peter Luhn and the Birth of the Hashing Algorithm" (2018)
  • "A brief history of the stack", SIGCIS (sigcis.org/files/A brief history.pdf: Bauer and Samelson applied for the patent in 1957, US 3,047,228 granted July 31, 1962; IPL list processing by Newell, Simon and Shaw at RAND 1956) and Wikipedia, Information Processing Language (Newell, Shaw, Simon, RAND and Carnegie Institute of Technology, about 1956).

Consultato il: 2026-10-18