Indice Prefazione alla quarta edizione CAPITOLO IX 1 Fondamenti di programmazione in Java 1.1 Per iniziare: classi, tipi e oggetti 1 1.1.1 Tipi di base 4 1.1.2 Oggetti 5 1.1.3 Tipi enumerativi 11 1.2 Metodi 12 1.3 Espressioni 17 1.3.1 Letterali 17 1.3.2 Operatori 17 1.3.3 Casting e autoboxing/unboxing nelle espressioni 20 1.4 Flusso di controllo 23 1.4.1 Le istruzioni if e switch 23 1.4.2 Cicli 25 1.4.3 Istruzioni esplicite di flusso di controllo 27 1.5 Gli array 29 1.5.1 Dichiarazione di array 30 1.5.2 Gli array sono oggetti 31 1.6 Semplici esempi di input e output 32 1.7 Un programma esemplificativo 35 1.8 Classi annidate e pacchetti 37 1.9 Stesura di un programma Java 40 1.9.1 Progetto 40 1.9.2 Pseudocodifica 40 1.9.3 Codifica 41 1.9.4 Testing e debugging 45 1.10 Esercizi 47 CAPITOLO 2 2.2 Ereditarietà e polimorfismo 55 2.3 2.4 2.5 2.6 CAPITOLO 3 Array, linked list e ricorsione 3.1 Impiego degli array 85 3.2 3.3 Progettazione orientata a oggetti 2.1 Obiettivi, princìpi e modelli 51 2.1.1 Obiettivi della progettazione orientata a oggetti 51 2.1.2 Princìpi della progettazione orientata a oggetti 52 2.1.3 Schema di progetto 54 2.2.1 Ereditarietà 55 2.2.2 Polimorfismo 57 2.2.3 Impiego dell’ereditarietà in Java 58 Le eccezioni 66 2.3.1 Lancio delle eccezioni 66 2.3.2 Cattura delle eccezioni 68 Interfacce e classi astratte 70 2.4.1 Realizzazioni delle interfacce 70 2.4.2 L’ereditarietà multipla nelle interfacce 72 2.4.3 Classi astratte 73 Casting e tipi generici 74 2.5.1 Casting 74 2.5.2 Tipi generici 78 Esercizi 80 3.4 3.1.1 Memorizzazione in un array degli elementi di un gioco 85 3.1.2 Ordinamento di un array 91 3.1.3 Metodi java.util per array e numeri casuali 93 3.1.4 Un semplice sistema crittografico mediante stringhe e array di caratteri 95 3.1.5 Array bidimensionali e giochi posizionali 97 Linked list semplici 101 3.2.1 Inserimento in una linked list semplice 102 3.2.2 Rimozione di un elemento in una linked list semplice 105 Linked list doppie 105 3.3.1 Inserimento intermedio in una linked list doppia 107 3.3.2 Rimozione di elementi intermedi di una linked list 109 3.3.3 Una implementazione della linked list doppia 110 Linked list circolari e ordinamento su linked list 112 VI INDICE © 978-88-08-07037-1 3.5 3.6 3.4.1 Linked list circolari e il gioco «papera, papera, oca» 112 3.4.2 Ordinamento di una linked list 116 Ricorsione 116 3.5.1 Ricorsione lineare 122 3.5.2 Ricorsione binaria 126 3.5.3 Ricorsione multipla 128 Esercizi 130 CAPITOLO 4 Strumenti per l’analisi 5.3 5.4 CAPITOLO 6.1 Array list 197 4.1.1 4.1.2 4.1.3 4.1.4 4.1.5 4.1.6 4.2 4.3 4.4 CAPITOLO 6.2 6.3 6.4 6.5 5 Stack e code 5.1 Gli stack 167 5.2 5.1.1 Il tipo di dato astratto stack 167 5.1.2 Una semplice implementazione di uno stack basata su array 169 5.1.3 Implementazione di uno stack mediante linked list 174 5.1.4 Inversione di un array mediante uno stack 175 5.1.5 Controllo delle parentesi e dei tag HTML 177 Le code 181 5.2.1 La coda come tipo di dato astratto 181 5.2.2 Una semplice implementazione basata su array 182 6 Le liste e gli iteratori 4.1 Le sette funzioni utilizzate in questo libro 135 La funzione costante 135 La funzione logaritmica 135 La funzione lineare 137 La funzione n log n 137 La funzione quadratica 137 La funzione cubica e altre funzioni polinomiali 139 4.1.7 La funzione esponenziale 140 4.1.8 Confronto delle velocità di incremento 141 Analisi degli algoritmi 142 4.2.1 Studi sperimentali 143 4.2.2 Operazioni primitive 145 4.2.3 Notazione asintotica 146 4.2.4 Analisi asintotica 150 4.2.5 Impiego della notazione O-grande 152 4.2.6 Un algoritmo ricorsivo per il calcolo delle potenze 155 Semplici tecniche di dimostrazione 156 4.3.1 Dimostrazione mediante esempi 156 4.3.2 Dimostrazione per contrapposizione e per assurdo 157 4.3.3 Induzione e invarianza rispetto a un ciclo 158 Esercizi 160 5.2.3 Implementazione di una coda mediante linked list 185 5.2.4 Lo scheduler round robin 186 Code doppie 187 5.3.1 La coda doppia come tipo di dato astratto 187 5.3.2 Implementazione di una coda doppia 189 Esercizi 192 6.6 6.1.1 L’array list come tipo di dato astratto 197 6.1.2 Il pattern adattatore 198 6.1.3 Una semplice implementazione basata sugli array 199 6.1.4 Una semplice interfaccia e la classe java.util.ArrayList 200 6.1.5 Implementazione di una array list mediante array estensibili 201 Node list 204 6.2.1 Operazioni basate sui nodi 205 6.2.2 Posizioni 205 6.2.3 Il tipo di dato astratto lista 206 6.2.4 Implementazione mediante liste doppiamente linkate 208 Iteratori 213 6.3.1 L’iteratore e i tipi di dati astratti iterabili 213 6.3.2 Il ciclo for-each di Java 216 6.3.3 Implementazione di iteratori 216 6.3.4 Liste e iteratori in Java 218 Le liste come TDA e la Collections Framework 220 6.4.1 La Collections Framework di Java 220 6.4.2 La classe java.util.LinkedList 221 6.4.3 Le sequenze 222 Caso di studio: l’euristica move-to-front 223 6.5.1 Utilizzo di una lista ordinata e di una classe innestata 224 6.5.2 Utilizzo di una lista con l’euristica move-to-front 224 6.5.3 Possibili utilizzi di una favorite list 230 Esercizi 230 CAPITOLO 7 Gli alberi 7.1 Alberi in generale 235 7.2 7.1.1 Terminologia e proprietà 235 7.1.2 L’albero come tipo di dato astratto (TDA) 238 7.1.3 Implementazione di un albero 239 Algoritmi fondamentali sugli alberi 241 VII INDICE © 978-88-08-07037-1 7.3 7.4 7.2.1 Profondità e altezza 241 7.2.2 Attraversamento preorder 244 7.2.3 Attraversamento postorder 246 Alberi binari 250 7.3.1 Il TDA albero binario 251 7.3.2 Interfaccia Java dell’albero binario 252 7.3.3 Proprietà degli alberi binari 253 7.3.4 Una struttura collegata per gli alberi binari 255 7.3.5 Rappresentazione di un albero binario mediante liste di array 262 7.3.6 Attraversamento di un albero binario 264 7.3.7 Il modello del template method 270 Esercizi 275 CAPITOLO 8.1 La coda a priorità come tipo di dato astratto 285 8.3 8.4 8.5 8.1.1 Chiavi, priorità e relazioni d’ordine totale 285 8.1.2 Entry e comparatori 287 8.1.3 Il TDA coda a priorità 289 8.1.4 Ordinamento con una coda a priorità 291 Implementazione di una coda a priorità mediante una lista 292 8.2.1 Implementazione con una lista non ordinata 292 8.2.2 Implementazione con una lista ordinata 293 8.2.3 Ordinamento per selezione e ordinamento per inserimento 295 Heap 297 8.3.1 La struttura dati heap 298 8.3.2 Alberi binari completi e loro rappresentazione 300 8.3.3 Implementazione di una coda a priorità mediante uno heap 305 8.3.4 Una implementazione Java dello heap 309 8.3.5 Ordinamento heap (heap-sort) 310 8.3.6 Costruzione dal basso verso l’alto (bottom-up) di uno heap* 313 Code a priorità adattabili 317 8.4.1 Metodi del TDA coda a priorità adattabile 317 8.4.2 Elementi consapevoli della posizione 318 8.4.3 Implementazione di una coda a priorità adattabile 319 Esercizi 319 CAPITOLO 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.2.6 9.3 8 Le code a priorità 8.2 9.2 Tabelle hash 333 9 Mappe e dizionari 9.1 La mappa come tipo di dato astratto 329 9.1.1 Una semplice implementazione di una mappa basata su una lista 332 9.4 9.5 9.6 I bucket array 333 Funzioni hash 334 Codici hash 334 Funzioni di compressione 337 Schemi per la gestione delle collisioni 339 Una implementazione Java di una tabella hash 343 9.2.7 Fattori di carico e ripetizione della funzione hash 343 9.2.8 Applicazione: conteggio della frequenza di parole 347 Il dizionario come tipo di dato astratto 348 9.3.1 Dizionari basati su liste e audit trail 350 9.3.2 Implementazione di un dizionario mediante tabella hash 352 9.3.3 Tabelle di ricerca ordinata e ricerca binaria 352 Skip list 356 9.4.1 Operazioni di ricerca e aggiornamento in una skip list 358 9.4.2 Un’analisi probabilistica delle skip list* 362 Estensioni e applicazioni dei dizionari 364 9.5.1 Entry consapevoli della posizione nei dizionari 364 9.5.2 Il TDA dizionario ordinato 365 9.5.3 Database dei voli e insiemi di massimi 366 Esercizi 369 CAPITOLO 10 Alberi binari di ricerca 10.1 Alberi binari di ricerca 375 10.1.1 Ricerca 376 10.1.2 Operazioni di aggiornamento 377 10.1.3 Implementazione in Java 381 10.2 Alberi AVL 385 10.2.1 Operazioni di aggiornamento 387 10.2.2 Implementazione in Java 391 10.3 Alberi estesi 395 10.3.1 Estensione 395 10.3.2 In quali circostanze occorre eseguire un’estensione 396 10.3.3 Analisi ammortizzata dell’estensione* 399 10.4 Alberi (2,4) 405 10.4.1 Alberi di ricerca multi-way 405 10.4.2 Operazioni di aggiornamento per gli alberi (2,4) 410 10.5 Alberi red-black 415 10.5.1 Operazioni di aggiornamento 417 10.5.2 Implementazione in Java 426 10.6 Esercizi 431 VIII INDICE © 978-88-08-07037-1 CAPITOLO 11 Ordinamento, insiemi e selezione 11.1 Merge-sort (ordinamento per fusione) 437 11.1.1 Dividi-e-conquista 437 11.1.2 Array e liste di fusione 441 11.1.3 Tempo di esecuzione del merge-sort 443 11.1.4 Implementazioni Java del merge-sort 445 11.1.5 Merge-sort ed equazioni ricorsive* 448 11.2 Quick-sort (ordinamento veloce) 449 11.2.1 Quick-sort probabilistico 455 11.2.2 Quick-sort in loco 456 11.3 Limite inferiore degli ordinamenti 459 11.4 Bucket-sort e radix-sort 461 11.4.1 Bucket-sort (ordinamento a secchi) 462 11.4.2 Radix-sort (ordinamento a radice) 463 11.5 Confronto tra algoritmi di ordinamento 464 11.6 Il TDA insieme e le strutture union/find 466 11.6.1 Semplice implementazione di un insieme 467 11.6.2 Partizioni con operazioni di union e find 469 11.6.3 Implementazione di una partizione basata su albero* 471 11.7 Selezione 474 11.7.1 Riduzione-e-ricerca 475 11.7.2 Selezione veloce probabilistica 475 11.7.3 Analisi della selezione veloce probabilistica 475 11.8 Esercizi 477 CAPITOLO 12 Elaborazione di testi 12.1 Operazioni su stringhe 485 12.1.1 La classe Java String 486 12.1.2 La classe Java StringBuffer 487 12.2 Algoritmi di pattern matching 488 12.2.1 Forza bruta 488 12.2.2 L’algoritmo di Boyer-Moore 489 12.2.3 L’algoritmo di Knuth-Morris-Pratt 492 12.3 I trie 496 12.3.1 I trie standard 498 12.3.2 I trie compressi 501 12.3.3 Trie suffissi 502 12.3.4 Motori di ricerca 505 12.4 Compressione di testi 506 12.4.1 L’algoritmo di codifica di Huffman 508 12.4.2 Il metodo greedy 508 12.5 Test di somiglianza del testo 509 12.5.1 Il problema della più lunga sottosequenza comune (LCS) 509 12.5.2 Programmazione dinamica 510 12.5.3 Applicazione della programmazione dinamica al problema LCS 510 12.6 Esercizi 513 CAPITOLO 13 I grafi 13.1 Il grafo come tipo di dato astratto 519 13.1.1 Il TDA grafo 524 13.2 Strutture dati per i grafi 524 13.2.1 La lista di lati 525 13.2.2 La lista delle adiacenze 527 13.2.3 La matrice delle adiacenze 528 13.3 Attraversamento dei grafi 530 13.3.1 Visita in profondità 530 13.3.2 Implementazione della visita in profondità 534 13.3.3 Visita in ampiezza 541 13.4 Grafi orientati 544 13.4.1 Attraversamento di un grafo orientato 546 13.4.2 Chiusura transitiva 548 13.4.3 Grafi orientati aciclici 551 13.5 Grafi pesati 554 13.6 Percorsi minimi 555 13.6.1 Algoritmo di Dijkstra 556 13.7 I minimum spanning tree 562 13.7.1 Algoritmo di Kruskal 566 13.7.2 L’algoritmo di Prim-Jarník 569 13.8 Esercizi 571 CAPITOLO 14 La memoria 14.1 Gestione della memoria 583 14.1.1 Gli stack nella macchina virtuale Java 583 14.1.2 Allocazione dello spazio nello heap di memoria 586 14.1.3 Garbage collection 588 14.2 Memoria esterna e memoria cache 590 14.2.1 La gerarchia delle memorie 590 14.2.2 Strategie di gestione della memoria cache 591 14.3 Ricerca esterna e alberi B-tree 595 14.3.1 Alberi (a, b) 596 14.3.2 B-alberi 598 14.4 Ordinamento nella memoria esterna 599 14.4.1 Fusione multi-way 600 14.5 Esercizi 601 Appendice A • Proprietà matematiche utili Bibliografia 613 Indice analitico 619 605