Sommario
Collezioni
• Una collezione (contenitore) è un oggetto che
raggruppa
gg pp ppiù dati in una singola
g
struttura. Vi sono
due tecniche fondamentali per rappresentare
collezioni di elementi: quella basata su strutture
i di i
indicizzate
(
(array,
compostii da
d elementi
l
i consecutivi)
i i)
e quella basata su strutture collegate (list, composte
da nodi: ogni nodo contiene dati e collegamenti ad
altri nodi).
• Collezioni:
– Array e liste.
– Nodi: dati e riferimenti.
riferimenti
• Liste:
– LinkedList: specifica e implementazione.
– Prestazioni.
Informatica Medica, I semestre, C++
• Le collezioni sono usate per memorizzare, leggere,
manipolare dati strutturati e per trasmettere tali dati
da un metodo ad un altro.
1
Informatica Medica, I semestre, C++
2
Collezioni
Collezioni
• Fino ad ora si sono usate le strutture dati array
(semplici e performanti). La loro caratteristica è di
avere celle memorizzate sequenzialmente che sono
accessibili attraverso indici con costo O(1).
• Tuttavia si può pensare di usare strutture dati in cui
ll’ordinamento
ordinamento dei dati è solo a livello logico (senza
usare un sottostante ordinamento fisico).
• Questo
Q t sii puòò realizzare
li
associando
i d ad
d ognii
elemento anche un puntatore all’elemento che lo
segue nella
ll sequenza.
• Per ora vediamo di realizzare questa idea di dati più
collegamenti: un oggetto (nodo) che contenga, oltre
all’informazione sul dato memorizzato, anche un
puntatore al nodo che contiene ll’elemento
elemento che nella
sequenza lo segue.
• Esempio di due nodi:
Informatica Medica, I semestre, C++
Informatica Medica, I semestre, C++
3
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link=0
4
Collezioni
Nodi
Struttura indicizzata (array)
v
‘ ’
‘a’
‘ ’
‘c’
‘ ’
‘a’
‘ ’
‘r’
‘f’
0
1
2
3
4
indice
• L’idea di dati più collegamenti è implementata con
una classe
l
Node.
• Lo stato di tale classe è descritto da un campo che
contiene
i
il dato
d
(
(per
esempio
i di tipo
i
float o
Persona) e un campo puntatore (Node *) al
nodo collegato.
collegato
• Il comportamento è descritto dai metodi per leggere
e scrivere lo stato,
stato oltre al costruttore e distruttore.
distruttore
Struttura collegata
s
data: ‘
d
‘a’
’
link
data: ‘r’
link
data: ‘a’
link
data: ‘f’
f
Link=0
data: ‘c’
link
Informatica Medica, I semestre, C++
5
Nodi: Node.h
#ifndef NODE_H
#define NODE_H
class Node{
char data;
Node *link;
public:
N d ( h
Node(char
d Node
d,
N d *l=0);
*l 0)
~Node();
char GetData()const;
Node * GetLink()const;
void SetData(char d);
void SetLink(Node *l);
};
#endif
Informatica Medica, I semestre, C++
• N
Nell’implementazione
ll’i l
i
che
h segue il tipo
i
d l dato
del
d
memorizzato nel nodo è char.
Informatica Medica, I semestre, C++
6
Nodi: Node.cpp
pp
La
dichiarazione
del
campo link di un nodo
può sembrare non corretta.
Tuttavia è corretta,
corretta ciò che
dichiara è un puntatore ad
uun ogge
oggettoo d
di tipo
po Node
ode e
non un oggetto stesso.
7
#include "Node.h"
Node.h
#include <iostream>
using namespace std;
Node::Node(char d, Node *l){
data=d;
link=l;
cout<<"Node("<<data<<")"<<endl;
}
Node::~Node(){
cout<<"~Node("<<data<<")"<<endl;
}
char Node::GetData()const{
return data;
}
Node * Node::GetLink()const{
return link;
}
void Node::SetData(char d){
d
data=d;
d
}
void Node::SetLink(Node *l){
link=l;
;
}
Informatica Medica, I semestre, C++
Vista la dichiarazione del
campo link
li k di un nodo,
d
nel costruttore non si deve
allocare un oggetto Node,
ma solo assegnare un
puntatore
passato
al
costruttore.
8
Nodi: test.cpp
pp
Si collega
ll
l’
l’oggetto
tt
nodo n ad un altro
oggetto nodo attraverso
il puntatore a tale nodo .
void test1(){
Node *n=new Node('A');
n->SetLink(new Node('B'));
cout<<n->GetData()<<" ";
cout<<n->GetLink()->GetData()<<endl;
delete n->GetLink();
delete n;
}
Si
deve
prima
distruggere ll’oggetto
oggetto
nodo collegato e poi il
nodo che contiene il
collegamento
ll
Attraverso n sii
Att
accede al contenuto
del nodo collegato
senza
il
nome
simbolico.
Node(A)
Node(B)
AB
~Node(B)
~Node(A)
9
Liste: introduzione
Informatica Medica, I semestre, C++
10
Liste: specifica
p
• Uno svantaggio delle liste concatenate consiste
nell’onerosità dell’accesso ai suoi elementi: l’unico
modo per raggiungere un elemento della lista è quello
di seguire le connessioni della lista dall’inizio in
modo sequenziale. Mentre negli array è possibile
accedere a qualsiasi elemento attraverso un indice.
p
delle liste sono diversi da
• I metodi di manipolazione
quelli visti per gli array.
generale una lista rende più semplice la
• In generale,
riorganizzazione degli elementi.
Informatica Medica, I semestre, C++
• Gli array presentano due limitazioni:
– Modificare
M difi
l dimensione
la
di
i
d ll’
dell’array
richiede
i hi d la
l
creazione di un nuovo array e la copia di tutti i dati
dall’array
dall
array originale a quello nuovo.
nuovo
– Inserire un elemento nell’array richiede lo
scorrimento di dati nell
nell’array
array.
• Queste limitazioni possono essere superate da
strutture concatenate: una collezione di nodi che
contengono dati e puntatori ad altri nodi.
nodi In
particolare si fa riferimento alle liste concatenate
((linked list).
)
Una possibile uscita
Informatica Medica, I semestre, C++
Liste: introduzione
11
• Una possibile interfaccia pubblica (comportamento) è
laa segue
seguente:
te:
I
Inserisce
i
l'
l'oggetto
x in
i fondo
f d alla
ll
lista.
class LinkedList{
...
public:
LinkedList();
~LinkedList();
bool Add(char x);
bool Remove(char x);
bool Contains(char x)const;
bool IsEmpty()const;
void Clear();
void Print() const;
};
Informatica Medica, I semestre, C++
Cancella
C
ll la
l prima
i
dell'oggetto x.
V ifi
Verifica
se
l'oggetto x.
l
la
li t
lista
occorrenza
contiene
ti
V ifi se la
Verifica
l lista
li t è logicamente
l i
t
vuota.
Svuota la lista.
lista
Stampa a monitor il contenuto
d ll lista.
della
li t
12
Liste: comportamento
Liste: esempio
p
Sia L un oggetto di tipo LinkedList: ecco alcune operazioni
Invocazione metodo
Stato della
lista
risultato
L.IsEmpty();
[]
true
L.Add(‘a’);
[a]
true
L.Add(‘b’);
( );
[a b]
true
L.Add(‘c’);
[a b c]
true
L.Contains(‘b’);
‘ ’
[a b c]
true
L.Remove(‘b’);
[a c]
true
L.Contains(‘b’);
[a c]
false
L.IsEmpty();
[[a c]]
false
Informatica Medica, I semestre, C++
• Si scriva una funzione che legge caratteri da tastiera e li
memorizza
i
i una lista.
in
li
L’inserimento
i
i
è terminato
i
d l carattere
dal
‘.’. Si stampi il contenuto della lista prima e dopo la
rimozione di un elemento.
elemento
• Utilizzando la lista,
lista non e necessario conoscere il numero di
caratteri da memorizzare: viene allocata la giusta quantità di
elementi.
• Quando si rimuove un elemento, non e necessario spostare una
parte degli elementi rimasti per riempire il vuoto lasciato dalla
rimozione (come si farebbe con gli array).
13
Liste: esempio
p
14
Liste: implementazione
p
void test2(){
Si utilizza un oggetto di tipo lista
LinkedList L;
conoscendo la sua interfaccia pubblica.
char
h
c;
N
Non
è necessario
i
sapere come è
cin>>c;
implementato: la lista è un ADT.
while(c!='.'){
L.Add(c);
Una possibile uscita
cin>>c;
ABC.
}
Node(A)
cout<<"---------"<<endl;
Node(B)
Node(C)
L.Print();
--------L.Remove('B');
ABC
L.Print();
~Node(B)
cout<<"---------"<<endl;
cout<<
<<endl;
AC
}
--------( )
~Node(A)
~Node(C)
Informatica Medica, I semestre, C++
Informatica Medica, I semestre, C++
15
• Una possibile realizzazione della lista consiste nel
considerare una sequenza di nodi che contengono
un dato e un puntatore al nodo successivo nella
sequenza. Tale sequenza è realizzata nella classe
LinkedList
che implementa l’interfaccia
mostrata nei lucidi precedenti.
gg
di tipo
p LinkedList è manipolato
p
• Un oggetto
utilizzando
i
metodi
resi
disponibili
dall’interfaccia ppubblica ((comportamento),
p
), non si
accede direttamente alla sequenza di nodi. È un
tipo
p di dato astratto,, ADT.
Informatica Medica, I semestre, C++
16
Nodi : operazioni principali, rimozione
Nodi: operazioni
p
p
principali
p
Node *current
current
• Le due principali operazioni da realizzare sulle liste
concatenate (sui nodi della lista nella fase di
implementazione)
p
) sono: la rimozione di un
elemento della lista e l’inserimento di un elemento
nella lista. Quindi diminuire o aumentare la
l
lunghezza
h
di una lista.
li
• La semplicità di tali operazioni è la ragione
d’essere delle liste concatenate: si modifica solo i
puntatori,
i inoltre
i l
solo
l quelli
lli relativi
l i i aii nodi
di da
d
manipolare.
Informatica Medica, I semestre, C++
17
Nodi : operazioni principali, inserimento
<<oggetto>>
Node
data: ‘A’
link
<<oggetto>>
Node
data: ‘C’
link
<<oggetto>>
Node
data: ‘B’
link=0
Il nodo da inserire viene messo nella catena di collegamenti
((l’operazione
p
è fatta sul nodo che si vuole inserire e sul nodo che
precede la posizione di inserimento):
tmp->SetLink(current->GetLink());
current->SetLink(tmp);
current
>SetLink(tmp);
<<oggetto>>
gg
Node
data: ‘A’
link
<<oggetto>>
gg
Node
data: ‘B’
link
Informatica Medica, I semestre, C++
<<oggetto>>
Node
data: ‘B’
link
<<oggetto>>
Node
data: ‘C’
link
Il nodo da rimuovere viene tolto dalla catena di collegamenti e
distrutto (l’operazione è eseguita sul nodo che precede quello che
si vuole rimuovere):
Node *tmp=current->GetLink();
current->SetLink(current->GetLink()->GetLink());
d l t tmp;
delete
t
<<oggetto>>
Node
data: ‘A’
link
<<oggetto>>
gg
Node
data: ‘B’
li k
link
<<oggetto>>
Node
data: ‘C’
link
Informatica Medica, I semestre, C++
18
Liste: implementazione
p
Node *tmp
tmp
Node *current
<<oggetto>>
Node
data: ‘A’
link
<<oggetto>>
gg
Node
data: ‘C’
link
19
• Vediamo una possibile implementazione della lista.
• Si usa un campo first che mantiene il puntatore al
primo nodo della lista e un campo last che
mantiene un puntatore all’ultimo nodo.
• L’ultimo nodo è caratterizzato dal campo link con
valore 0.
• Il costruttore crea una lista vuota: cioè i due campi
puntatori hanno valore 0.
• Il distruttore deve deallocare i nodi ancora presenti
nella lista.
Informatica Medica, I semestre, C++
20
Liste: implementazione
p
Liste: LinkedList.h
#ifndef LINKEDLIST_H
#define LINKEDLIST_H
#include "Node.h"
class LinkedList{
Node * first;
Node * last;
public:
LinkedList();
~LinkedList();
bool Add(char x);
bool Remove(char x);
bool Contains(char x)const;
bool IsEmpty()const;
void Clear();
void Print() const;
};
#endif
• Una possibile rappresentazione grafica:
<<oggetto>>
LinkedList
first
last
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
…
<<oggetto>>
Node
data
link=0
Informatica Medica, I semestre, C++
21
Liste: LinkedList.cpp
pp
#include "Node.h"
#include "LinkedList.h"
#include <iostream>
using
g namespace
p
std;
;
LinkedList::LinkedList(){
(){
first=last=0;
}
LinkedList:: LinkedList(){
LinkedList::~LinkedList(){
Clear();
}
Informatica Medica, I semestre, C++
Informatica Medica, I semestre, C++
22
Liste: LinkedList.cpp
pp
bool LinkedList::Contains(char x)const
{
for(Node *i=first; i!=0; i=i->GetLink())
if (i->GetData()==x)
return true;
return false;
}
La lista viene
creata vuota: non
esiste nessun nodo
collegato
collegato.
Il
distruttore
deve
deallocare i nodi ancora
presenti nella lista,, q
p
quindi
deve eseguire un Clear().
In tal modo si fattorizza il
codice, cioè non si duplica
esplicitamente il codice ma
si richiama il metodo
opportuno
opportuno.
23
Per verificare la presenza di un dato oggetto x nella lista, si
devono esaminare i nodi della lista.
lista Si può fare con un ciclo for:
si definisce il contatore i come un puntatore di tipo Node* a cui si
assegna
g il p
puntatore al p
primo nodo. Si esce dal ciclo q
quando si
finiscono i nodi, cioè il link vale 0. L’incremento si ottiene con
l’assegnamento i=i->GetLink(), che fa puntare il contatore i al
nodo
d collegato.
ll
Informatica Medica, I semestre, C++
24
Liste: ricerca
Liste: LinkedList.cpp
pp
Node *i=first;
bool LinkedList::IsEmpty()const{
return first
first==0;
0;
}
i
first
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
bool LinkedList::Add(char x)
{
Lista vuota: modifico
if (IsEmpty())
(
())
entrambi i campi
first=last=new Node(x);
else{
last->SetLink(new Node(x));
last=last->GetLink();
}
Lista
s non
o vuo
vuota:: modifico
od co
return true;
solo in campo che punta
}
i=i->GetLink()
i
i >GetLink()
i
first
<<oggetto>>
Node
data
li k
link
<<oggetto>>
Node
data
li k
link
<<oggetto>>
Node
data
li k
link
La lista è vuota se il
campo che
h punta
t all
primo nodo vale 0
all’ultimo nodo
Informatica Medica, I semestre, C++
25
Liste:
inserimento
last
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link=0
Lista non vuota:
last->SetLink(new Node(x));
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link=0
last=last->GetLink();
last
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
Informatica Medica, I semestre, C++
26
Liste: prestazioni
p
last
<<oggetto>>
Node
data
link
Informatica Medica, I semestre, C++
• Aggiungere un nodo alla lista ha un costo computazionale
costante O(1):
( ) è indipendente
i di d
d l numero di nodi
dal
di nella
ll
lista. Non si devono copiare gli elementi in una nuova
struttura come nel caso di array pieni.
struttura,
pieni
• Verificare se la lista contiene un dato oggetto implica l’uso
di un ciclo for. Si consideri il caso medio: se ogni nodo
della lista ha la stessa probabilità di contenere il dato,
allora si ha una iterazione per verificare il primo nodo, due
iterazioni per verificare il secondo nodo, e in una sequenza
lunga n si ha in media
n
1
1 n(n  1)
 O ( n)
i

2
n i 1
n
<<oggetto>>
Node
data
link=0
27
Informatica Medica, I semestre, C++
28
Liste: LinkedList.cpp
bool LinkedList::Remove(char x)
{
L’
L’operazione
i
di rimozione
i
i
è eseguita
i
if (IsEmpty()) return false;
sul nodo che precede quello che si
if (first->GetData()==x)
vuole rimuovere
{
if (first==last) last=0;
Node *tmp=first->GetLink();
Controllo il primo elemento
delete first;
first=tmp;
first
tmp;
return true;
}
for (Node *i=first; i->GetLink()!=0; i=i->GetLink())
{
if (i->GetLink()->GetData()==x)
Se non è il primo elemento
{
if (i
(i->GetLink()==last)
>GetLink() last)
last=i;
Node *tmp=i->GetLink();
i->SetLink(i->GetLink()->GetLink());
delete tmp;
return true;
}
}
return false;
29
} Informatica Medica, I semestre, C++
<<oggetto>>
Node
data
link
LINKEDLIST: rimozione
<<oggetto>>
Node
data
link
Primo elemento
<<oggetto>>
Node
data
link
Node *tmp=first->GetLink();
delete first;
first
tmp
<<oggetto>>
Node
data
link
first=tmp;
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
first
<<oggetto>>
Node
data
link
<<oggetto>>
Node
data
link
Informatica Medica, I semestre, C++
30
Liste: LinkedList.cpp
Liste: prestazioni
p
• Considerazioni analoghe a quelle sviluppate per
calcolare la complessità computazionale media del
metodo per la ricerca di un dato valgono anche per
il metodo di rimozione di un dato.
• Quindi il metodo Remove() ha in media un costo
lineare cioè O(n).
lineare,
O(n)
Informatica Medica, I semestre, C++
first
31
void LinkedList::Clear()
{
Node *i=first;
while(i!=0)
{
Node *tmp=i->GetLink();
delete i;
i=tmp;
}
first=0;
last 0;
last=0;
}
Per svuotare la lista si devono deallocare
tutti i nodi presenti e aggiornare lo stato.
void LinkedList::Print()const
{
for(Node *i=first; i!=0; i=i->GetLink())
cout<<i->GetData()<<"
cout<<i
>GetData()<< ";
;
cout<<endl;
}
Informatica Medica, I semestre, C++
32
Liste: considerazioni
Liste doppiamente
pp
concatenate
• Se è necessario un accesso immediato a ciascun
elemento,
l
allora
ll
l’
l’array
è la
l scelta
l migliore.
i li
• Se si accede frequentemente solo ad alcuni
elementi
l
i (i primi,
i i gli
li ultimi)
l i i) e se la
l modifica
df
d ll
della
struttura è il cuore di un algoritmo, allora la lista
concatenata è la scelta migliore.
migliore
• Un vantaggio delle liste è che usano la quantità
minima di memoria necessaria e sono facilmente
ridimensionabili. Al contrario un array può essere
modificato in dimensione solo di quantità fisse
(per esempio dimezzato o raddoppiato).
Informatica Medica, I semestre, C++
33
Liste con salti
• Per trovare un elemento in una lista è necessaria
uno scansione sequenziale. Anche se la lista è
ordinata è sempre necessaria una scansione
sequenziale.
questo pproblema utilizzando una lista con
• Si evita q
salti: liste che consentano di saltare alcuni nodi
pper evitare manipolazioni
p
sequenziali.
q
• La struttura interna è più complessa: nodi con un
numero di campi riferimento diverso.
diverso
Informatica Medica, I semestre, C++
35
• Esistono altri tipi di lista, vediamone alcuni.
• La lista descritta è di tipo semplicemente
concatenata:
t t i nodi
di hanno
h
i f
informazione
i
solo
l sull
loro successore, per cui non si ha accesso al loro
predecessore.
predecessore
• Si può definire una lista doppiamente concatenata
in modo che ogni nodo abbia due campi
riferimento: uno al successore e uno al
p
predecessore.
• In tal modo è possibile percorrere la lista nei due
sensi.
Informatica Medica, I semestre, C++
34