Una lista è una struttura dati che organizza una collezione di elementi in una sequenza ordinata. Le liste sono ampiamente utilizzate in programmazione per memorizzare dati in modo dinamico e flessibile.
Esistono diversi tipi di liste, tra cui:
null
. Le operazioni di inserimento e rimozione in una lista collegata sono efficienti, specialmente all’inizio o alla fine della lista.null
, creando un ciclo continuo. Questo permette di attraversare la lista senza fine.Le liste forniscono una flessibilità notevole perché possono crescere e contrarsi dinamicamente durante l’esecuzione del programma. Possono anche contenere elementi di diversi tipi di dati e possono essere facilmente estese con nuove operazioni o funzionalità.
Le liste sono ampiamente utilizzate per implementare una varietà di strutture dati complesse, come pile, code, alberi e grafi, e sono fondamentali in molte aree della programmazione, inclusi algoritmi, strutture dati e applicazioni di database.
La classe Nodo
rappresenta un elemento fondamentale di una lista collegata. Ogni nodo contiene due parti principali:
info
): Questo è il contenuto effettivo del nodo. È il valore che il nodo memorizza o rappresenta.link
): Questo è un riferimento al prossimo nodo nella lista. Ogni nodo, tranne l’ultimo, punta al nodo successivo. L’ultimo nodo della lista ha il suo link impostato su null
, indicando la fine della lista.Inoltre, la classe Nodo
fornisce metodi per accedere e modificare questi attributi:
getInfo()
: Restituisce il valore memorizzato nel nodo.getLink()
: Restituisce il riferimento al nodo successivo.setLink(Nodo link)
: Imposta il riferimento al nodo successivo.public class Nodo {
private int info; // valore del nodo corrente
private Nodo link; // puntatore al nodo successivo
public Nodo(int info) {
this.info = info;
this.link = null; // non ha ancora un nodo successivo
}
public int getInfo() {
return info;
}
public Nodo getLink() {
return link;
}
public void setLink(Nodo link) {
this.link = link; // setto il nodo successivo
}
public void setInfo(int info) {
this.info = info;
}
}
La classe Lista
rappresenta una sequenza di nodi collegati. Contiene due parti principali:
head
): Questo è un riferimento al primo nodo della lista. Se la lista è vuota, head
sarà null
. Ogni operazione sulla lista inizia dalla testa.dimensione
): Questo tiene traccia del numero di elementi presenti nella lista. È utile per ottenere rapidamente la dimensione della lista senza dover attraversare tutti i nodi.La classe Lista
offre metodi per manipolare la lista collegata:
getDimensione()
: Restituisce il numero di elementi nella lista.VisitaLista()
: Stampa tutti gli elementi presenti nella lista, scorrendo da head
fino a null
.InserisciInTesta(int dato)
: Aggiunge un nuovo nodo all’inizio della lista. Questo metodo crea un nuovo nodo con il dato specificato e lo collega alla testa della lista, rendendolo il nuovo primo nodo.InserisciInCoda(int dato)
: Aggiunge un nuovo nodo alla fine della lista. Questo metodo scorre la lista fino all’ultimo nodo e collega il nuovo nodo alla sua fine.EliminaInTesta()
: Rimuove il primo nodo dalla lista. Questo metodo aggiorna la testa della lista per puntare al secondo nodo, eliminando così il primo nodo.EliminaInCoda()
: Rimuove l’ultimo nodo dalla lista. Questo metodo scorre la lista fino al penultimo nodo e lo collega a null
, eliminando così l’ultimo nodo.Insieme, la classe Nodo
e la classe Lista
forniscono un’implementazione di una lista collegata, una struttura dati dinamica in cui gli elementi sono collegati da nodi e possono essere aggiunti o rimossi in modo efficiente.
public class Lista {
private Nodo head; // punta al primo elemento della lista (null se vuota)
private int dimensione; // numero di elementi nella lista
public Lista() {
this.head = null;
this.dimensione = 0;
}
// restituisce la dimensione della lista
public int getDimensione() {
return dimensione;
}
// stampa
public void VisitaLista() {
Nodo p = head;
// itero fino a trovare l'ultimo elemento della lista
while (p != null) {
System.out.print(p.getInfo() + " ");
p = p.getLink();
}
}
public void InserisciInTesta(int dato) {
Nodo n = new Nodo(dato); // nuovo nodo
n.setLink(head); // Il link del nuovo nodo punta al nodo corrente (head)
head = n; // Il nuovo nodo diventa la nuova testa della lista
dimensione++;
}
public void InserisciInCoda(int dato) {
Nodo n = new Nodo(dato); // nuovo nodo
if (head == null) { // caso in cui la lista e' vuota
head = n;
} else {
Nodo p = head; // Nodo al primo elemento della lista
while (p.getLink() != null) {
p = p.getLink();
}
n.setLink(null);
p.setLink(n);
}
dimensione++;
}
public void InserisciInMezzo(int dato, int posizione) {
if (posizione < 0 || posizione > dimensione)
throw new IllegalArgumentException("Errore: Posizione non valida");
Nodo n = new Nodo(dato); // nuovo nodo
if (posizione == 0) {
// inserimento in testa
n.setLink(head);
head = n;
} else {
Nodo p = head;
for (int i = 0; i < posizione-1; i++) {
p = p.getLink();
}
n.setLink(p.getLink());
p.setLink(n);
}
dimensione++;
}
public void EliminaInTesta() {
// se la lista e' vuota non posso eliminare nulla
if (head == null)
return;
head = head.getLink();
dimensione--;
}
public void EliminaInCoda() {
if (head == null)
return;
if (head.getLink() == null) { // caso in cui la lista contiene un solo elemento
head = null;
} else {
Nodo p = head; // nodo di testa
// p.getList() -> nodo i; p.getLink().getLink() -> nodo i+1
while (p.getLink().getLink() != null) {
p = p.getLink();
}
p.setLink(null);
}
dimensione--;
}
public void EliminaInMezzo(int posizione) {
if (posizione < 0 || posizione > dimensione)
throw new IllegalArgumentException("Errore: Posizione non valida");
if (head == null)
throw new IllegalStateException("Errore: Lista gia' vuota");
if (posizione == 0) {
// elimina in testa
head = head.getLink();
} else {
Nodo p = head;
for (int i = 0; i < posizione - 1; i++) {
p = p.getLink();
}
p.setLink(p.getLink().getLink());
}
dimensione--;
}
}
public class Main {
public static void main(String[] args) {
// Creazione di una nuova lista
Lista lista = new Lista();
// Inserimento di alcuni elementi
lista.InserisciInCoda(10);
lista.InserisciInCoda(20);
lista.InserisciInCoda(30);
// Stampa della lista
System.out.println("Lista dopo l'inserimento:");
lista.VisitaLista();
// Eliminazione in testa
lista.EliminaInTesta();
System.out.println("\nLista dopo l'eliminazione in testa:");
lista.VisitaLista();
// Eliminazione in coda
lista.EliminaInCoda();
System.out.println("\nLista dopo l'eliminazione in coda:");
lista.VisitaLista();
// Inserimento in testa
lista.InserisciInTesta(40);
System.out.println("Lista dopo l'inserimento in testa:");
lista.VisitaLista();
// Inserimento in mezzo
lista.InserisciInMezzo(25, 1);
System.out.println("Lista dopo l'inserimento in mezzo:");
lista.VisitaLista();
// Eliminazione in mezzo
lista.EliminaInMezzo(1);
System.out.println("Lista dopo l'eliminazione in mezzo:");
lista.VisitaLista();
}
}
ESERCIZIO 1: Un dispsitivo MP3 consente di gestire playlist di brani musicali descritti da titolo e durata espressa in secondi. Implementare in linguaggio Java le classi Brano e Playlist che consentano di eseguire le seguenti operazioni, gestendo in modo adeguato le relative eccezioni:
aggiunta di un brano alla Lista dall’ultima posizione;
eliminazione dalla lista di un brano identificato dal titolo;
determinazione della durata totale dei brani della lista;
esportazione dei primi n brani della lista in un vettore ( con n fornito come parametro);
esportazione dei brani della lista fino a un tempo complessivo t di riproduzione (con t fornito come parametro);
spostamento di un brano identificato dalla posizione (2, 3, …, n) nella posizione precedente (1, 2, …, n - 1);
spostamento di un brano identificato dalla posizione (1, 2, …, n - 1) nella posizione successiva (2, 3, …, n);
salvataggio e ripristino della lista dei brani in/da un file di tipo testuale;
riordino casuale dei brani della lista (funzione shuffle: il metodo nextint della classe java.util.Random restituisce un numero casuale intero).
Realizzare un main che consenta di eseguire le operazioni in modo interattivo.