Sei sulla pagina 1di 23

Tipi di Dato Astratti

Liste

Prof. Giuseppe Riccardi


Dipartimento Ingegneria e Scienza delle Informazioni
Università degli Studi di Trento

Indice

•  Tipi di Dato Astratti


•  Liste

1
Tipi di Dato Astratto
Abstract Data Type (ADT)

• Tipo di Dato : insieme di valori di una


variabile
• ADT è un modello matematico che include le
operazioni definite nel modello
• ADT possono essere implementati in uno
specifico linguaggio di programmazione (LP)
• Uso di strutture dati per rappresentare ADT in
uno specifico LP

Liste(1)
•  Quando dobbiamo scandire una collezione di
oggetti in modo sequenziale e non sequenziale,
un modo conveniente per rappresentarli è quello
di organizzare gli oggetti in un array.
•  Esempi:
† (1, 2, -3, 5, -10) è una sequenza di interi.
† (‘a’, ‘d’, ‘1’, ‘F’) è una sequenza di caratteri.

2
Liste(1)
•  Una soluzione alternativa all’uso di array per
rappresentare le liste quando l’accesso non sequenziale
non è un requisito, consiste nell’uso delle cosiddette liste
(concatenate).
•  In una lista concatenata i vari elementi che
compongono la sequenza di dati sono rappresentati in
zone di memoria che possono anche essere distanti fra
loro (al contrario degli array, in cui gli elementi sono
consecutivi).
•  In una lista concatenata, ogni elemento contiene
informazioni necessarie per accedere all’elemento
successivo.

Liste
•  Vantaggi rispetto all’array:
†  Flessibilità
di modifica
†  Riduzione e aumento dinamico della dimensione .
†  Memorizzo solo quello che mi serve. Mentre con array potrei
sprecare memoria (array sovradimensionato).
•  Svantaggi rispetto all’array:
†  Lo
svantaggio principale consiste nell’onerosità nell’accesso ai
suoi elementi.
•  Unico modo per raggiungere un dato elemento consiste nello
scorrere la lista dal nodo iniziale.

3
Liste
•  Modello matematico
†  Una lista L e’ una sequenza di zero o più elementi di un tipo
di dato ( TipoElemento )
a1,a2,…an
†  Il numero di elementi n di tale lista è detta lunghezza.
†  Se n ≥ 1, a1 è il primo elemento
†  Se n =0, la lista è vuota
†  Ordine lineare secondo posizione nella lista:
•  a1 precede a2
•  a4 segue a3
†  Utile definire esistenza di FINE(L)
†  La distanza tra primo elemento lista e FINE(L)
varierà nel tempo

Lista dinamica
Implementazione

•  Costruzione e gestione della struttura dinamica lista


mediante puntatori:

Elemento
Puntatore
1 della
a elemento
lista 2 Puntatore NULL
Ultimo
Lista elemento
e1 e2 en

Puntatore alla
“testa di lista”“testa di lista” “coda di lista”

4
Dichiarazione lista dinamica (1)

•  Invece di dichiarare il tipo lista, si dichiarano i suoi


elementi:
struct EL {
TipoElemento Info;
struct EL *Prox;
};

typedef struct EL ElemLista;

typedef ElemLista *ListaDiElem;

Dichiarazione lista dinamica (2)


•  Dichiarazione standard; mette in evidenza il tipo della lista:

ListaDiElem Lista1, Lista2, Lista3;

•  Dichiarazioni abbreviate:


ElemLista *Lista1;

se non interessa mettere in evidenza il tipo della lista, ma si vuole


indicare esplicitamente il tipo dei suoi elementi.


struct EL *Lista1

può sostituire entrambe le typedef se non è necessario nominare


esplicitamente né il tipo della lista né il tipo dei suoi elementi.

5
Liste - Operazioni
•  Inserisci elemento a nella lista L
†  Testa, Coda, Lista Ordinata,
•  Ricerca elemento a nella lista L
•  Cancella elemento a dalla lista L
•  Estrai
†  testadella lista L
†  coda della lista L
†  successore di elemento nella lista L
†  predecessore di elemento nella lista L

Operazioni sulle liste: Inizializzazione (1)

•  Assegna il valore NULL alla variabile “testa della lista”:


l’operazione:
•  Lista=NULL;

Lista

•  Se però vogliamo eseguire l’operazione in maniera


parametrica: …

6
Inizializzazione (3) senza parametro

#include <stdlib.h>

ListaDiElem Lista1;

void Inizializza(void)
{
Lista1 = NULL;
}

•  Inizializzazione tramite accesso alla variabile globale


•  Si raccomanda di evitare questa prassi!

Inizializzazione (2)
#include <stdlib.h>

void Inizializza (ListaDiElem *ListaPtr)


/* Lista è la variabile locale che punta alla "testa di
lista". La funzione assegna alla “testa di lista" il
valore NULL corrispondente al valore di lista vuota */
{
*ListaPtr = NULL;
}
ListaPtr
•  L’istruzione
ListaDiElem Lista1
Lista1
Inizializza(&Lista1);
produce:

7
Controllo di lista vuota
boolean ListaVuota (ListaDiElem Lista)
/* Produce il valore true se la lista passata
come parametro è vuota, false in caso
contrario, a Lista viene passato il valore
contenuto nella variabile testa di lista.
Lista punta pertanto al primo elemento della
lista considerata */
{
if (Lista == NULL) return true;
else return false;
}

•  La chiamata sarà:

ListaVuota (Lista1)

Ricerca di un elemento nella lista


boolean Ricerca (ListaDiElem Lista, TipoElemento ElemCercato)
{
ElemLista *Cursore;

if (Lista != NULL)
{
Cursore = Lista; /* La lista non è vuota */
while (Cursore != NULL)
{
if (Cursore–>Info == ElemCercato) return true;
Cursore = Cursore–>Prox;

/* In questa maniera Cursore viene fatto


puntare all'elemento successivo della lista */
}
}
return false;
}

8
Ricerca di un elemento nella lista
boolean Ricerca (ListaDiElem Lista, TipoElemento ElemCercato)
{
ElemLista *Cursore;

if (Lista != NULL)
{
Cursore = Lista; /* La lista non è vuota */
while (Cursore != NULL)
{
if (Cursore–>Info == ElemCercato) return true;
Cursore = Cursore–>Prox;

/* In questa maniera Cursore viene fatto


puntare all'elemento successivo della lista */
}
}
return false;
}

Ricerca di un elemento nella lista


boolean Ricerca (ListaDiElem Lista, TipoElemento ElemCercato)
{
ElemLista *Cursore;

if (Lista != NULL)
{
Cursore = Lista; /* La lista non è vuota */
while (Cursore != NULL)
{
if (Cursore–>Info == ElemCercato) return true;
Cursore = Cursore–>Prox;

/* In questa maniera Cursore viene fatto


puntare all'elemento successivo della lista */
}
}
return false;
}

9
Ricerca di un elemento nella lista
boolean Ricerca (ListaDiElem Lista, TipoElemento ElemCercato)
{
ElemLista *Cursore;

if (Lista != NULL)
{
Cursore = Lista; /* La lista non è vuota */
while (Cursore != NULL)
{
if (Cursore–>Info == ElemCercato) return true;
Cursore = Cursore–>Prox;

/* In questa maniera Cursore viene fatto


puntare all'elemento successivo della lista */
}
}
return false;
}

Ricerca di un elemento nella lista,


versione ricorsiva
boolean Ricerca (ListaDiElem Lista,
TipoElemento ElemCercato)
{
if (Lista == NULL)
return false;
else
if (Lista–>Info == ElemCercato)
return true;
else
return Ricerca(Lista–>Prox, ElemCercato);
}

10
Ricerca di un elemento nella lista,
versione ricorsiva
boolean Ricerca (ListaDiElem Lista,
TipoElemento ElemCercato)
{
if (Lista == NULL)
return false;
else
if (Lista–>Info == ElemCercato)
return true;
else
return Ricerca(Lista–>Prox, ElemCercato);
}

Ricerca di un elemento nella lista,


versione ricorsiva
boolean Ricerca (ListaDiElem Lista,
TipoElemento ElemCercato)
{
if (Lista == NULL)
Soluzioni

return false;
Base

else
if (Lista–>Info == ElemCercato)
return true;
else
return Ricerca(Lista–>Prox, ElemCercato);
}

11
Ricerca di un elemento nella lista,
versione ricorsiva
boolean Ricerca (ListaDiElem Lista,
TipoElemento ElemCercato)
{
if (Lista == NULL)
Soluzioni

return false;
Base

else
if (Lista–>Info == ElemCercato)
return true;
else
return Ricerca(Lista–>Prox, ElemCercato);
}

Ricerca di un elemento nella lista,


versione ricorsiva
boolean Ricerca (ListaDiElem Lista,
TipoElemento ElemCercato)
{
if (Lista == NULL)
Soluzioni

return false;
Base

else
if (Lista–>Info == ElemCercato)
return true;
else
return Ricerca(Lista–>Prox, ElemCercato);
}

12
Accesso testa e coda di una lista
TipoElemento TestaLista (ListaDiElem Lista)
/* È applicabile solo a liste non vuote. Se la lista è vuota segnala
l'errore in modo opportuno; in caso contrario produce come
risultato il valore del campo Info del primo elemento della lista */
ListaDiElem CodaLista (ListaDiElem Lista)
/*Produce come risultato un puntatore all'elemento che segue elemento di
testa. Essa non deve modificare il parametro originario. Anche questa
assume l'ipotesi che il parametro passatole sia una lista non vuota */

CodaLista

Ultimo
Lista elemento

e1 e2 en

Inserimento nuovo elemento in testa (1)

Punt

Punt = malloc(sizeof(ElemLista));
Punt–>Info = Elem;

Punt
Elem

Lista
e1 e2 … en

13
Inserimento nuovo elemento in testa (2)

•  Infine si collega il nuovo elemento al precedente primo


elemento della lista e la testa della lista viene fatta puntare al
nuovo elemento:
Punt

Elem

Lista
e1 e2 en

Inserimento nuovo elemento in testa (3)

void InserisciInTesta (ListaDiElem *Lista,


TipoElemento Elem)
{
ElemLista *Punt;
/* Allocazione dello spazio necessario per la memorizzazione
del nuovo elemento e inizializzazione del puntatore */
Punt = malloc(sizeof(ElemLista));
Punt–>Info = Elem;
Punt–>Prox = *Lista;
*Lista = Punt;
}
Lista

e1 e2 en
Lista1

14
Inserimento nuovo elemento in testa (3)

void InsercisciInTesta (ListaDiElem *Lista,


TipoElemento Elem)
{
ElemLista *Punt;
/* Allocazione dello spazio necessario per la memorizzazione
del nuovo elemento e inizializzazione del puntatore */
Punt = malloc(sizeof(ElemLista));
Punt–>Info = Elem;
Punt–>Prox = *Lista;
*Lista = Punt; Punt
} Elem
Lista

e1 e2 en
Lista1

Inserimento nuovo elemento in testa (3)

void InsercisciInTesta (ListaDiElem *Lista,


TipoElemento Elem)
{
ElemLista *Punt;
/* Allocazione dello spazio necessario per la memorizzazione
del nuovo elemento e inizializzazione del puntatore */
Punt = malloc(sizeof(ElemLista));
Punt–>Info = Elem;
Punt–>Prox = *Lista;
*Lista = Punt;
}
Lista Punt
Elem

e1 e2 en
Lista1

15
Inserimento nuovo elemento in testa (3)

void InsercisciInTesta (ListaDiElem *Lista,


TipoElemento Elem)
{
ElemLista *Punt;
/* Allocazione dello spazio necessario per la memorizzazione
del nuovo elemento e inizializzazione del puntatore */
Punt = malloc(sizeof(ElemLista));
Punt–>Info = Elem;
Punt–>Prox = *Lista;
*Lista = Punt;
}
Lista Punt
Elem

e1 e2 en
Lista1

Inserimento nuovo elemento in coda (1)


Ricorsiva

16
Lista*1 Lista*2 Lista*3 Lista*n Lista*n+1 Cont.

e1 e2 en-1 en

Lista1

Lista*1 Lista*2 Lista*3 Punt


Lista*n+1
Elem

e1 e2 en-1 en

Lista1

Punt
Lista*1 Lista*2 Lista*3 Lista*n+1
Elem

e1 e2 en-1 en

Lista1

Inserimento nuovo elemento in coda (1)


Ricorsiva
void InserisciInCoda (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt;

if (ListaVuota (*Lista))
{
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = NULL;
Punt–>Info = Elem;
*Lista = Punt;
}
else InserisciIncoda (&((*Lista)–>Prox), Elem);
}

17
Inserimento nuovo elemento in coda (2)
Ricorsiva
void InserisciInCoda (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt;

if (ListaVuota (*Lista))
{
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = NULL;
Punt–>Info = Elem;
*Lista = Punt;
}
else InserisciIncoda (&((*Lista)–>Prox), Elem);
}

Inserimento nuovo elemento in coda (3)


Ricorsiva
void InserisciInCoda (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt;

if (ListaVuota (*Lista))
{
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = NULL;
Punt–>Info = Elem;
*Lista = Punt;
}
else InserisciIncoda (&((*Lista)–>Prox), Elem);
}

18
Inserimento nuovo elemento in coda (3)
Ricorsiva
void InserisciInCoda (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt;

if (ListaVuota (*Lista))
{
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = NULL;
Punt–>Info = Elem;
*Lista = Punt;
}
else InserisciIncoda (&((*Lista)–>Prox), Elem);
}

Inserimento elemento in lista


ordinata
•  Su TipoElemento e’ definita una relazione d’ordine
•  int à >
•  Tipi strutturati à MinoreDi

19
Cont.

39

Inserimento nuovo elemento in


lista ordinata
void InserisciInOrdine (ListaDiElem *Lista,
TipoElemento Elem);
/* Modifica lista , passata per indirizzo, e assumendo
ordinata in ordine crescente “>”. Inserisce Elem in
modo da mantenerla ordinata. Nessun controllo su
ripetizione Elem nella lista */

20
Inserimento nuovo elemento in
lista ordinata
void InserisciInOrdine (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt, *PuntCorrente, *PuntPrecedente;
PuntPrecedente=NULL;
PuntCorrente=*Lista;
while ((PuntCorrente != NULL) && (Elem > PuntCorrente->Info)
{
PuntPrecedente = PuntCorrente;
PuntCorrente = PuntCorrente->Prox;
}
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = PuntCorrente;
Punt–>Info = Elem;
if(PuntPrecedente != NULL) PuntPrecedente->Prox=Punt;
` else *Lista=Punt;
}

Inserimento nuovo elemento in


lista ordinata
void InserisciInOrdine (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt, *PuntCorrente, *PuntPrecedente;
PuntPrecedente=NULL;
PuntCorrente=*Lista;
while ((PuntCorrente != NULL) && (Elem > PuntCorrente->Info)
{
PuntPrecedente = PuntCorrente;
PuntCorrente = PuntCorrente->Prox;
}
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = PuntCorrente;
Punt–>Info = Elem;
if(PuntPrecedente != NULL) PuntPrecedente->Prox=Punt;
` else *Lista=Punt;
}

21
Inserimento nuovo elemento in
lista ordinata
void InserisciInOrdine (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt, *PuntCorrente, *PuntPrecedente;
PuntPrecedente=NULL;
PuntCorrente=*Lista;
while ((PuntCorrente != NULL) && (Elem > PuntCorrente->Info)
{
PuntPrecedente = PuntCorrente;
PuntCorrente = PuntCorrente->Prox;
}
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = PuntCorrente;
Punt–>Info = Elem;
if(PuntPrecedente != NULL) PuntPrecedente->Prox=Punt;
` else *Lista=Punt;
}

Inserimento nuovo elemento in


lista ordinata
void InserisciInOrdine (ListaDiElem *Lista,
TipoElemento Elem);
{
ElemLista *Punt, *PuntCorrente, *PuntPrecedente;
PuntPrecedente=NULL;
PuntCorrente=*Lista;
while ((PuntCorrente != NULL) && (Elem > PuntCorrente->Info)
{
PuntPrecedente = PuntCorrente;
PuntCorrente = PuntCorrente->Prox;
}
Punt = malloc(sizeof(ElemLista));
Punt–>Prox = PuntCorrente;
Punt–>Info = Elem;
if(PuntPrecedente != NULL) PuntPrecedente->Prox=Punt;
` else *Lista=Punt;
}

22
Sommario
•  Liste in C realizzate mediante puntatori
•  Puntatori: struttura di basso livello ---> a rischio
•  Liste: primo importante esempio di struttura dinamica
•  Efficienza della struttura dinamica lista rispetto all’array:
•  Pros: Si evita lo spreco di memoria/rischio di overflow
(obiettivo iniziale)
•  Cons: Overhead dovuto ai puntatori
•  Da un punto di vista del tempo necessario all’esecuzione
degli algoritmi: pro e contro (inserire in testa meglio,
inserire in coda peggio
•  Ricerca in una lista ordinata penalizzata dalla ricerca sequenziale.

23

Potrebbero piacerti anche