Strutture dati elementari
Dott. Ezio Bartocci, Dott. Francesco De Angelis
Laboratorio di Algoritmi e strutture Dati - AA 2006/2007
Dip. di Matematica e Informatica
Università di Camerino
[email protected], [email protected]
24 novembre 2006
Introduzione
Costrutti di base
Array
Liste concatenate
Strutture dati e algoritmi
La decisione più importante nella fase di implementazione di
un’applicazione è la scelta della struttura più oppurtuna per
rappresentare i dati.
Per uno stesso insieme di dati, vi sono strutture dati che
richiedono più spazio di altre.
Per uno stesso insieme di operazioni sui dati, alcune strutture
dati portano a implementare algoritmi più efficienti di altri.
2 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Costrutti di base
Tutti i dati che vengono elaborati su un calcolatore alla fine sono
scomposti in singoli bit, ma scrivere programmi che elaborino
direttamente questi bit sarebbe quantomeno noioso.
I tipi ci consentono di specificare come andremo a usare un
particolare insieme di bit, mentre i metodi permettono di definire
le operazioni che verranno eseguite sui dati.
Usiamo le classi di Java per descrivere le informazioni che
elaboriamo, per definire i metodi che agiscono su queste
informazioni e per costruire oggetti che le memorizzano
effettivamente.
3 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Strutture dati
Le stutture dati che andremo a vedere sono formate da oggetti e
da riferimenti a oggetti.
Quando scriviamo programmi, di solito vogliamo elaborare
informazioni derivanti da qualche descrizione formale
(matematica) o informale del mondo in cui viviamo.
Gli ambienti di programmazione devono già avere in sè gli
elementi di base di queste descrizioni, vale a dire numeri e
caratteri.
4 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Strutture dati semplici
Tipi di dati primitivi in Java
boolean - valori booleani
char - caratteri
byte - numeri interi a 8 bit
short - numeri interi a 16 bit
int - numeri interi a 32 bit
long - numeri interi a 64 bit
float - numeri in virgola mobile a 32 bit
double - numeri in virgola mobile a 64 bit
5 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Tipi di dati standard
Definition
Un tipo di dato è definito da un insieme di valori e da una collezione
di operazioni su questi valori.
Le operazioni sono associate ai tipi e non viceversa.
Quando eseguiamo un operazione, dobbiamo assicurare che
tanto gli operandi quanto il risultato siano del tipo corretto.
In alcuni casi, Java esegue conversioni in modo automatico; in
altri usiamo il cast, cioè l’esplicita conversione di tipo. Ad
esempio, se x ed N sono interi, questa espressione include
entrambi i tipi di conversione: ((float) x) / N
6 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Operazioni sui tipi di dati standard
Molte delle operazioni associate ai tipi di dati standard (i.e. le
operazioni aritmetiche) sono già in effetti incorporate nel
linguaggio Java, altre operazioni sono implementate sotto forma
di metodi in librerie standard di Java, altre ancora sono costituite
dai metodi Java definiti nei programmi che scriviamo.
Spesso abbiamo bisogno di definire i nostri tipi di dati per
organizzare i programmi in maniera efficace.
7 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Classi
Esempio di classe
class Studente
{
private String nome;
private String cognome;
private int anno;
private int matricola;
//Costruttore
Studente(String nome,
String cognome,
int anno, int matricola){
this.nome = nome;
this.cognome = cognome;
this.anno = anno;
this.matricola = matricola;
}
int getMatricola(){ return matricola;}
int getAnno(){ return anno; }
public String toString(){
return nome + " " + cognome;
}
}
Tutti i programmi in Java sono basati
sul meccanismo delle classi.
La classe definisce tutte le proprietà
degli oggetti appartenenti a quella
classe, detti attributi, e le funzioni
che verranno usate per agire su di
essi, detti metodi.
Le classi sono dei prototipi di oggetti,
ovvero sono delle strutture astratte
che possono essere instanziate
creando uno o più oggetti.
8 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Programmazione object-oriented
Possiamo pensare alle classi come a un meccanismo che ci
consente non solo di aggregare dati, ma anche di definire
operazioni su quei dati.
Anche se ci sono diversi oggetti che appartengono a una classe,
tutti questi oggetti sono simili nel senso che i valori assumibili dai
loro dati membro sono gli stessi e che l’insieme delle operazioni
che possono essere eseguite sui dati membro è lo stesso.
Nella programmazione object-oriented sono gli oggetti che
elaborano i loro dati membro (invece di avere metodi liberi che
agiscono sui dati memorizzati negli oggetti)
9 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Classi e metodi
Definizione di un metodo
class LogTable{
static int lg (int N){
for (int i=0; N > 0; i++){ N/=2;}
return i;
}
public static void main (String[] args){
for (int N=100; N < 10000; N*=10)
Out.println(lg(N) + " " + N);
}
}
Programma Java più semplice
class HelloWorld{
public static void main (String[] args){
System.out.println("Hello World !!"); }
}
In Java, per implementare nuove
operazioni sui dati, definiamo metodi in
classi.
Il metodo lg nell’esempio implementa
una funzione matematica a un solo
argomento che corrisponde al logaritmo
intero in base 2.
In Java, gli argomenti vengono detti
parametri e il valore della funzione è
chiamato valore di ritorno.
Ogni programma Java è una classe che
include la definizione del metodo main. Il
programma Java più semplice che si
possa scrivere è quello formato dal solo
metodo main.
10 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Polimorfismo
Overloading degli operatori
class ValoreAssoluto
{
static int abs(int value){
return value >= 0 ? value : -value;
}
static double abs(double value){
return value >= 0 ? value : -value;
}
}
La definizione di un metodo inizia
con la sua segnatura, che definisce
il tipo del suo valore di ritorno, il suo
nome e i tipi dei suoi parametri.
L’uso di nomi identici per metodi
differenti si dice overloading, se il
sistema è in grado di distinguerli
tramite le differenze nelle loro
segnature.
I tipi dei parametri e la presenza o
l’assenza di un valore di ritorno
possono servire a distinguere tra
loro metodi aventi lo stesso nome.
11 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Ereditarietà
Classe Punto
class Point{
double x, y;
Point {
x = Math.random();
y = Math.random();
}
}
Classe punto etichettato
class LabeledPoint extends Point{
String id;
void label(String name){id = name;}
public String toString(){
return id + "(" + x + ", " + y + ")";}
}
Spesso ci troviamo a dover costruire un
nuovo tipo di dato arricchendone uno già
esistente.
Per agevolare questo tipico compito Java
offre la possibilità di definire una classe
che ne estende un’altra.
Il tipo di dato definito dalla classe estesa
è determinato dai membri della classe
base più tutti i membri della classe
estesa.
La classe estesa può tanto definire nuovi
membri quanto ridefinire membri della
classe base.
12 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Array
Definition
Un array è un insieme fissato di elementi dello stesso tipo memorizzati
in modo contiguo e accessibili per mezzo di un indice.
L’Array in Java come nella maggiorparte dei linguaggi di
programmazione è definito come primitiva di linguaggio.
Denoteremo l’i-esimo elemento di un array a con a[i].
13 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Operare su un Array
Crivello di Eratostene
class Primes{
public static void main(String[] args){
int N = Integer.parseInt(arg[0]);
int[] a = new int[N];
for (int i=2; i < N; i++) a[i]= 1;
for (int i=2; i < N; i++)
if (a[i] != false)
for (int j=i; j*i < N; j++)
a[i*j] = 0;
for (int i=2; i < N; i++)
if (i > N - 100)
if (a[i]) Out.print(" " + i);
Out.println();
}
}
Example
i
2
3
4
5
6
7
8
9
10
11
12
2
1
1
1
1
1
1
1
1
1
1
1
3
5
a[i]
1
1
0
1
0
1
0
0
0
1
0
0
14 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Allocazione affidabile di un array
Example
int[] a;
try {
a = new int[N];
}
catch (OutOfMemoryError e){
Out.println("Out of memory"); return;
}
}
Se nel programma precedente
digitassimo come argomento sulla
riga di comando un numero
estremamente grande, si
produrrebbe nel sistema
un’eccezione OutOfMemoryError
(mancanza di memoria)
E’ buona pratica di programmazione
tenere sotto controllo tutti gli errori
che possono capitare.
Quindi potrebbe essere utile
sostituire le righe del codice che
creano l’array di interi con il codice
qui a fianco.
15 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Array di Oggetti
1.7 1.0
3.5 2.0
Array di oggetti Points
2.3 6.7
0
class ClosePoints{
public static void main (String[] args){
int cnt = 0;
N = Integer.parseInt(args[0]);
double d = Double.parseDouble(args[1]);
Point[] a = new Point[N];
for (int i = 0; i < N; i++)
a[i] = new Point();
for (int i = 0; i < N; j++)
if (a[i].distance(a[j]) < d) cnt++;
Out.print(cnt + " pairs " );
Out.println(" closer than " + d);
}
1
2.0 6.1
2
3
5.0 4.0
4
5
6
6.1 3.9
1.4 3.2
In Java, un array di oggetti è, in realtà, un
array di riferimenti a oggetti, come
mostrato dalla figura qui sopra
16 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Lista concatenata
Quando abbiamo necessità di scandire una collezione di elementi
in modo sequenziale, uno dopo l’altro, una scelta conveniente è
quella di organizzare gli elementi in una lista concatenata.
In una lista concatenata, ogni elemento contiene le informazioni
necessarie per accedere all’elemento successivo.
Vantaggi rispetto all’Array
Flessibilità di modifica
Svantaggi rispetto all’Array
Onerosità nell’accesso ai suoi elementi: l’unico modo per raggiungere un dato elemento
della lista è quello di seguire le connessioni della lista dall’inizio
17 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Definizione
Definition
Una lista concatenata è un insieme di elementi, dove ogni elemento
è inserito in un nodo contenente anche un link (cioè una connessione
o riferimento) a un (altro) nodo.
Le liste sono dette a volte strutture autoreferenzianti proprio per
aver definito i nodi in termini di riferimenti ad altri nodi.
Sebbene di solito i link di un nodo puntino ad altri nodi, tali link
potrebbero anche puntare al nodo medesimo, e quindi dar luogo
a strutture circolari.
18 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Vediamo in Java
Classe Node
class Node{
Object item;
Node next;
Node(Object v) {
item = v;
next = null;
}
}
Adotteremo queste convenzioni
per il link del nodo finale:
è un link nullo che non porta ad alcun
nodo
punta ad un nodo fittizio che non
contiene alcun elemento
punta indietro al primo nodo della lista,
creando quindi una lista circolare
Instanziare una lista di 2 nodi
//x is an Object
Node t = new Node(x);
punta indietro al primo nodo della lista,
creando quindi una lista circolare.
19 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Cancellazione e Inserimento in una lista
Cancellazione
Inserimento
t = x.next;
x.next = t.next;
//oppure x.next = x.next.next;
t.next = x.next; x.next = t;
t
t
x
t
x
x
t
x
t
x
20 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Esempio di lista circolare
Problema di Giuseppe Flavio
class Node {
int val; Node next;
Node (int v){ val = v;}
}
class Josephus{
public static void main (String args[]){
int N = Integer.parseInt(args[0]);
int M = Integer.parseInt(args[1]);
Node t = new Node(1);
Node x = t;
for (int i = 2; i <= N; i++){
x = (x.next = new Node(i));
x.next = t;
while (x != x.next){
for (int i = 1; i < M; i++)
x = x.next;
x.next = x.next.next;
}
Out.println("L’eletto è " + x.val);
}
}
Immaginiamo che N persone debbano
eleggere un leader nel modo seguente
le persone si dispongono in cerchio
eliminano una persona ogni M,
seguendo l’ordine del cerchio e
richiudendo il cerchio ad ogni
eliminazione.
Il problema è quello di scoprire
quale persona rimarrà per ultima.
21 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Risultato dell’elezione di Giuseppe Flavio
4
3
2
3
4
1
2
3
4
1
5
2
9
6
9
6
7
8
7
3
4
6
9
6
2
8
8
8
7
8
3
2
6
9
9
2
2
6
9
8
8
2
8
9
8
22 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Definizione più restrittiva
Definition
Una lista concatenata consta di un link nullo oppure di un link ad un
nodo, che contiene un elemento e un link a una lista concatenata.
Questa definizione è più restrittiva di quella precedente, ma
corrisponde in modo più preciso all’idea che abbiamo di una lista
quando scriviamo codice per elaborarla.
23 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Attraversamento di una lista
Codice per attraversare una lista
for (Node t = x; t != null; t = t.next)
visit(t.item);
Una delle più comuni operazioni su liste
è quella dell’attraversamento: scandiamo
tutti gli elementi della lista in maniera
sequenziale, eseguendo una qualche
operazione su ognuno dei nodi
Ad esempio, se xreferenzia il primo nodo
di una lista, l’ultimo nodo ha il link a
NULLe visitè un metodo che prende
un elemento come parametro, possiamo
scrivere il codice riportato qui a fianco
24 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Inversione di un lista
Metodo Reverse
Node reverse (Node x){
Node t, y = x, r = null;
while (y != null){
t = y.next;
y.next = r;
r = y;
y = t;
}
return r;
}
Questo metodo inverte i link di una lista,
restituendo un puntatore al nodo finale il quale a
sua volta punta al penultimo, e così via. Il link
del primo nodo della lista originale è posto a
NULL. Per effettuare le operazioni è necessario
mantenere link a tre nodi consecutivi nella lista.
r
y
t
r
y
25 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Ordinamento per insersione in una lista
Class Node
class Node {
int val; Node next;
Node(int v, Node t) {
val = v;
next = t;
}
}
Metodo create()
public Node create(){
Node a = new Node (0, null);
for (In.init(); !In.empty();)
a.next= new Node(In.getInt(), a.next);
return a;
}
Metodo sort()
Node sort(Node a){
Node t, u, x, b = new Node(o, null);
while (a.next != null) {
t = a.next; u = t.next; a.next = u;
for (x = b; x.next != null; x = x.next)
if (x.next.val > t.val) break;
t.next = x.next; x.next = t;
}
return b;
}
26 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Risultato dell’ordinamento
Manteniamo un puntatore t al primo
nodo della lista non ordinata (in alto)
627
a
758
113
t
101
515
838
b
758
Quindi, scandendo la lista puntata
da b, cerchiamo il primo nodo x con
x.next.item > t.item (oppure con
x.next=null) e inseriamo t nella lista
appena dopo x.
113
a
Queste operazioni riducono di 1 la
lunghezza della lista a e aumentano
di 1 quella della lista b, mantenendo
quest’ultima in ordine (in basso).
627
758
b
t
838
x
758
a
101
b
113
113
515
627
838
Ripetendo il procedimento,
giungiamo prima o poi a esaurire a
ed avere in bla lista ordinata dei
nodi.
27 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Convenzioni su testa e coda in liste concatenate
Coda circolare, mai vuota
primo inserimento:
inserisci t dopo x:
cancella dopo x:
ciclo di attraversamento:
testa se un solo elemento:
head.next = head;
t.next = x.next; x.next = t;
x.next = x.next.next;
t = head
do { · · · t = t.next; } while (t != head);
if (head.next == head)
28 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Convenzioni su testa e coda in liste concatenate
Riferimento in testa, coda a null
inizializza:
inserisci t dopo x:
cancella dopo x:
ciclo di attraversamento:
testa se vuota:
head = null
if (x == null) { head = t; head.next = null; }
else { t.next = x.next; x.next = t; }
t = x.next; x.next = t.next;
for (t = head; t != null; t = t.next)
if (head == null)
29 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Convenzioni su testa e coda in liste concatenate
Nodo fittizio in testa, coda a null
inizializza:
inserisci t dopo x:
cancella dopo x:
ciclo di attraversamento:
testa se vuota:
head = new Node();
head.next = null;
t.next = x.next; x.next = t;
t = x.next; x.next = t.next;
for (t = head; t != null; t = t.next)
if (head == null)
30 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Convenzioni su testa e coda in liste concatenate
Nodi fittizi in testa e in coda
inizializza:
inserisci t dopo x:
cancella dopo x:
ciclo di attraversamento:
testa se vuota:
head = new Node();
z = new Node();
head.next = z; z.next = z;
t.next = x.next; x.next = t;
x.next = x.next.next;
for (t = head; t != null; t = t.next)
if (head.next == z)
31 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Lista doppiamente concatenata
Inserimento
Cancellazione
t.next.prev = t.prev;
t.prev.next = t.next;
t.next = x.next;
x.next.prev = t;
x.next = t; t.prev = x;
t
t
x
t
t
x
t
x
32 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Ordinamento di stringhe con lista
3
n o w 2
i
s
3
t
h e
4
t
i m e
3
f o
r
3
a l
l
3
n o w 2
i
s
3
t
h e
4
t
i m e
3
f o
r
3
a l
l
0
1
2
3
4
5
0
1
2
3
4
5
33 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Se non basta: Una multilista
758
a
838
113
627
101
b
515
34 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Grafi e liste di adiacenza
0
6
2
1
7
3
4
5
Grafo
0 1 2 3 4 5 6 7
0
1
2
3
4
5
6
7
1
1
1
0
0
1
1
1
1
1
0
0
0
0
0
1
1
0
1
1
0
0
0
1
0
0
0
1
1
1
0
0
0
0
0
1
1
1
1
1
1
0
0
0
1
1
0
0
1
0
0
0
1
0
1
0
1
1
1
0
0
0
0
1
Rappresentazione del
grafo con una matrice d'adiacenza
0
7
5
1
7
0
2
7
0
3
5
4
4
6
5
0
6
4
0
1
2
7
2
1
5
7
3
4
3
0
6
4
Rappresentazione del
grafo con liste di adiacenza
35 / 36
Introduzione
Costrutti di base
Array
Liste concatenate
Riferimenti
[Sed03] §3 - Strutture Dati Elementari
36 / 36