...

Liste, Pile e Code

by user

on
Category: Documents
12

views

Report

Comments

Transcript

Liste, Pile e Code
Liste Pile e Code
Pag. 1/18
Liste, Pile e Code
Le liste, le pile e le code rappresentano tre ADT elementari che ricorrono
frequentemente nella vita reale.
Esempi usuali di una lista, una pila ed una coda sono rispettivamente una lista della
spesa, una pila di piatti e una coda in un pubblico esercizio. La prima rappresenta un
insieme di oggetti da acquistare che possono essere scritti (inseriti) o depennati
(cancellati) senza alcuna particolare regola. La seconda anch’essa rappresenta un
insieme di oggetti, però gestiti attraverso l’applicazione di una regola pratica: prelevo in
testa ed inserisco in testa. Infine l’ultima è gestita mediante una regola dettata dal vivere
civile: la prima persona che entra è la prima persona ad essere servita!
Nell’esamina dei tre ADT introdurremo un altro concetto, quello di struttura dati,
termine che utilizzeremo quando nella disamina ci occuperemo sia della organizzazione
delle informazioni che della efficiente implementazione di un operatore; concetti che
come vedremo sono fortemente connessi tra loro.
1. L’ADT Lista
Una lista consiste in una sequenza di n oggetti ( a1, …, an ) appartenenti ad un insieme
S. La lunghezza di una lista è determinata dal numero dei suoi elementi. Definiamo
vuota una lista che ha lunghezza zero.
Un elemento di una lista è caratterizzato da una posizione: indichiamo con ai l’elemento
in posizione i-esima all’interno di una lista L=( a1, a2, …, an ).
Esempio 1.1
Esempi di lista sono:
L1 = ( 71, 21, 39 ) dove Lunghezza(L) = 3 e a1 = 71, a2 = 21, a3 = 39
L2 = ( 23, 45, 139, 13 ) dove Lunghezza(L) = 4 e a1 = 23, a2 =45, a3 = 139, a4 = 13
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 2/18
Le operazioni tipiche su una lista sono la ricerca di un elemento, l’estrazione di un
elemento presente in una data posizione, l’inserimento e la cancellazione. Utilizzando i
formalismi dell’ADT:
Tipo di dato: Lista
Insieme L di ‘n’ elementi estratti da un insieme S
Operazioni: CercaChiave( elemento e )Æposizione
Cerca l’elemento ‘e’ all’interno della lista L e restituisce l’indice
compreso tra 1 e la lunghezza della lista nel caso in cui e viene trovato,
altrimenti restituisce 0.
CercaPosizione( posizione i )Æelemento
Restituisce l’elemento in posizione i-esima se i è un valore compreso tra
1 e la lunghezza della lista
Inserisci( elemento e, posizione i )
Inserisce l’elemento’ e’ all’interno della lista L in posizione’ i’
Cancella( posizione i )
Cancella l’elemento presente all’interno della lista L nella posizione ’ i’
2.1 Implementazione mediante vettore
La struttura dati lista indicizzata rappresenta l’implementazione dell’ADT lista
mediante l’uso di un vettore.
Tale struttura dati richiede la definizione di un vettore V di dimensione prefissata, m,
dove l'elemento i-esimo della lista, ai, occupa la posizione ‘i-1’.
VÆ
1
6
2
23
3
L=(1, 6, 2, 23, 3)
contatore=4
Gestiamo le posizioni libere del vettore mediante l’utilizzo di una variabile, contatore,
tramite la quale teniamo traccia della dimensione corrente della lista. Tale variabile
assume inizialmente il valore 0, viene incrementata ad ogni operazione di inserimento e
decrementata ad ogni operazione di cancellazione.
Poiché la dimensione m del vettore deve essere prestabilita, la struttura dati lista
indicizzata non consente la rappresentazione di liste contenenti più di m elementi,
ovvero la struttura dati lista indicizzata è non dinamica.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 3/18
L’organizzazione delle informazioni contenute all’interno della lista, così come
dettate dalla struttura dati lista indicizzata, influenza ad ogni modo la realizzazione degli
operatori dichiarati nella specifica. Vediamo come.
Inserimento
L'inserimento di un elemento nel vettore V alla posizione i richiede, prima di poter dar
luogo all’operazione, lo spostamento a destra di tutti gli elementi aventi indice maggiore
o uguale ad i-1. In tal modo liberiamo la cella (i-1)-esima del vettore, all’interno della
quale possiamo memorizzare l’elemento da inserire.
VÆ
1
6
VÆ
1
6
VÆ
1
6
2
50
Contatore = 5
23
3
2
23
3
Contatore = 5
2
23
3
Contatore = 6
Figura 2.1 – Inserimento in posizione 3 dell’elemento 50
Nel caso pessimo, inserimento in prima posizione, il numero di passi da eseguire è
dipendente dal numero di elementi presenti all’interno della lista. Al crescere del
numero degli elementi, crescono in maniera linearmente proporzionale il numero di
spostamenti verso destra da eseguire. Diremo quindi che l’operazione di inserimento
richiede tempo linearmente proporzionale ad n.
Cancellazione
La cancellazione di un elemento in posizione ‘i’ prevede, contrariamente
all’operazione di inserimento, uno spostamento a sinistra degli elementi aventi indice
maggiore ad i-1. Nel caso pessimo, rappresentato dalla cancellazione in prima
posizione, l’operazione di cancellazione richiede un tempo proporzionale ad n.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 4/18
CercaChiave
La ricerca della chiave richiede la scansione, a partire dalla prima posizione,
dell’intero vettore. Pertanto, nel caso pessimo, elemento non presente, tale operazione
richiede tempo proporzionale ad n.
CercaPosizione
Diamo luogo all’operazione di CercaPosizione restituendo il valore presente alla
posizione i-esima del vettore.
Il costo di tale operazione è indipendente dal numero di elementi presenti all’interno
della lista. Il tempo di accesso alla cella i-esima è indipendente dal numero di elementi
presenti nel vettore. Diremo in tal caso che l’operazione di CercaPosizione richiede
tempo costante.
2.2 Implementazione mediante puntatori
La struttura dati lista concatenata specifica una organizzazione delle informazioni
contenute all’interno della lista alternativa a quella della struttura dati lista indicizzata.
Necessitiamo in tal caso della definizione di un record contenente un campo chiave ed
un campo che denomineremo successivo.
chiave
succ.
Figura 2.2 – Record
Infine, realizziamo la lista definendo n record, uno per ogni elemento della lista, in
modo tale che l'i-esimo record contenga all’interno del campo chiave l'elemento ai della
lista e all’interno del campo successivo l'indirizzo del record contenente l’elemento ai+1
della lista. Il campo successivo dell'ultimo record assume il valore NULL.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 5/18
Gestiamo le dimensioni della lista utilizzando una variabile, Testa, che punta l’elemento
posto ad inizio lista. Tale variabile assume inizialmente il valore NULL.
Testa
a1
a2
...
an
null
Figura 2.3 – lista concatenata
Il vantaggio di questo tipo di implementazione è dato dal fatto che non vi sono vincoli
sulla dimensione della lista; l'unico vincolo è dato dalla quantità di memoria disponibile.
Come già fatto per la struttura dati lista indicizzata, analizziamo ora come tale
rappresentazione influenzi l’implementazione degli operatori dell’ADT Lista.
Inserimento
Per inserire un elemento nel vettore V alla posizione i occorre:
•
•
•
•
•
creare (ovvero, allocare dinamicamente) un nuovo record;
assegnare al campo chiave del nuovo record il valore dell’elemento;
scandire la lista sino a giungere al record i-esimo;
assegnare al campo successivo del record precedente quello i-esimo l'indirizzo
del nuovo record;
assegnare al campo successivo del nuovo record l'indirizzo del record i-esimo.
Poiché nel caso peggiore, inserimento in ultima posizione, occorre scandire l'intera
lista, l'operazione di inserimento richiede tempo proporzionale alla dimensione corrente
della lista.
Testa
a1
a2
...
an
null
a
Figura 2.4 – Inserimento nella lista concatenata
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 6/18
Cancellazione
La cancellazione necessita dell’utilizzo di due puntatori, p e q. Ci si sposta con uno, p,
sull’elemento i-esimo e con l’altro, q, sull’elemento precedente a quello i-esimo.
Dopodichè il campo successivo del record puntato da q sarà posto uguale al campo
successivo del record puntato da p ed infine viene deallocato il record puntato da p.
q
p
Testa
a1
a2
a3
a4
...
an
null
Figura 2.5 – Cancellazione in una lista concatenata
Poiché nel caso peggiore, cancellazione in ultima posizione, occorre scandire l'intera
lista, l'operazione di cancellazione richiede tempo proporzionale alla dimensione
corrente della lista.
CercaChiave
La ricerca della chiave necessita della scansione della lista con l’utilizzo di un
puntatore che salta da elemento in elemento seguendo l’informazione memorizzata nel
campo successivo di ogni record. Nel caso pessimo, chiave non esistente, l’operazione
di ricerca richiede tempo proporzionale alla dimensione della lista.
CercaPosizione
L’operazione di ritorno dell’elemento presente in posizione i-esima ha un tempo
anch’esso proporzionale alla dimensione della lista, poichè con l’utilizzo di un puntatore
che salta da elemento in elemento, seguendo le informazioni presenti nel campo
successivo, dobbiamo dapprima posizionarci sul record i-esimo e poi ritornare il valore
assunto dal campo chiave.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 7/18
3. L’ADT Lista Ordinata
Una lista ordinata L è una sequenza di n elementi (al, ...,an ) tratti da un insieme
totalmente ordinato S. Gli elementi di una lista ordinata sono regolati dalla seguente
relazione: ai ≤ ai+1 ∀ i∈{1, … n-1 }.
La presenza di un ordinamento degli elementi non rende applicabile l'operatore di
inserimento in posizione arbitraria. Gli operatori tipici per una lista ordinata sono
l’operatore di ricerca, l’operatore di inserimento e l’operatore di cancellazione.
Tipo di dato: Lista ordinata
Insieme L di ‘n’ elementi ( al, ...,an ) estratti da un insieme S totalmente
ordinato e tali che ai ≤ ai+1 ∀ i∈{1, … n-1 }
Operazioni: Cerca( elemento e )Æposizione
Cerca l’elemento ‘e’ all’interno della lista L e restituisce l’indice
compreso tra 1 e la lunghezza della lista nel caso in cui e viene trovato,
altrimenti restituisce 0
Inserisci( elemento e )
Inserisce l’elemento ’e’ all’interno della lista L
Cancella( elemento e )
Cancella l’elemento presente all’interno della lista L
3.1 Implementazione mediante vettore
L’utilizzo della struttura dati lista indicizzata rappresenta la pratica più comune per la
rappresentazione di una lista ordinata. In tal caso, similmente all’ADT Lista,
necessitiamo di un vettore V di dimensioni fisse, all’interno del quale ospitiamo gli
elementi della lista ordinata in ordine crescente.
Una variabile contatore indicherà la dimensione corrente della lista.
Vediamo come l’utilizzo di una simile struttura dati influenzi l’implementazione degli
operatori di ricerca, inserimento e cancellazione.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 8/18
Ricerca
La ricerca per chiave avviene attraverso l’algoritmo di ricerca binaria. Sfruttiamo la
caratteristica di ordinamento degli elementi dell’ADT dando luogo ai seguenti passi:
o confronto: confrontiamo il valore da ricercare, k, con il valore, v,
presente alla posizione centrale della porzione di vettore effettivamente
utilizzata (mediana)
ƒ
se k = v, allora l'elemento è stato trovato
ƒ
se k < v allora ricerchiamo l'elemento ricorsivamente nella prima
metà del vettore poiché esso contiene elementi minori di k. Ci
disinteressiamo dell'altra metà dell'insieme
ƒ
se k > v allora ricerchiamo l'elemento ricorsivamente nella
seconda metà del vettore poiché esso contiene solo elementi
maggiori di k. Ci disinteressiamo anche in questo caso dell'altra
metà dell'insieme
Ricerca Binaria
RicercaBinaria( elemento k, intero iniziovettore, intero finevettore )
{
Lista L
Vettore V
se finevettore == iniziovettore allora
{
se V[iniziovettore] != k allora
elemento non trovato
altrimenti
elemento trovato
}
mediana = (finevettore-iniziovettore)/2
se V[mediana] = k allora
{
elemento trovato
}
altrimenti
{
se V[mediana] >= k allora
RicercaBinaria( k, iniziovettore, mediana-1 )
altrimenti
RicercaBinaria( k, mediana+1, finevettore )
}
}
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 9/18
Inserimento
Diamo luogo all’operazione di inserimento mediante l’esecuzione dei seguenti quattro
passi:
o ricerchiamo l'elemento contenente la chiave assegnata. Tale operazione
restituisce un indice i che rappresenta la posizione che la chiave
dovrebbe occupare;
o eseguiamo uno Shift a destra degli elementi aventi indice maggiore di i;
o inseriamo il nuovo elemento in posizione i;
o incrementiamo la variabile contatore.
Cancellazione
L'operazione di cancellazione consiste di tre fasi:
-
ricerchiamo l'elemento contenente la chiave assegnata. Tale operazione
restituisce un indice i che rappresenta la posizione che la chiave occupa;
-
operiamo lo Shift a sinistra degli elementi aventi indice maggiore di i;
-
decrementiamo la variabile contatore.
E’ dimostrabile che se adoperiamo l’algoritmo di ricerca binaria, l’operazione di
ricerca di un elemento in una lista ordinata richiede nel caso pessimo, elemento non
trovato, un tempo logaritmico nel numero di elementi.
Mentre, a causa delle operazioni di shift, sia l’inserimento che la cancellazione
richiedono nel caso pessimo, inserimento e cancellazione in prima posizione, un tempo
lineare con il numero di elementi della lista.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 10/18
4. Il tipo di dato astratto Pila
Una pila è una collezione di elementi che ammette esclusivamente due operazioni:
•
PUSH: inserimento di un elemento ad una estremità, detta sommità;
•
POP: estrazione dell'elemento presente nella sommità.
PUSH
POP
Elemento 3
Elemento 2
Elemento 1
Figura 4.1: pila
Possiamo considerare una pila come una specializzazione dell’ADT lista in cui
l'ultimo elemento inserito è anche il primo ad essere estratto.
Denominiamo con l’acronimo LIFO (Last In First Out) la politica di gestione degli
elementi di una pila.
Tipo di dato:
Pila
Sequenza P di ‘n’ elementi ( al, ...,an ) estratti da un insieme S
Operazioni:
IsEmpty()Æresult
Restituisce true se P è vuota altrimenti restituisce false
Push( elemento e )
Inserisce l’elemento ’e’ in testa alla pila P
Pop()Æelemento
Restituisce l’elemento in testa alla pila P
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 11/18
4.1 Implementazione tramite vettore
Possiamo rappresentare l’ADT Pila mediante la struttura dati Lista Indicizzata. E’ una
soluzione facilmente applicabile. Definiamo un vettore V, di dimensione prefissata,
MAX, ed una variabile, Testa, che punta in testa alla pila. Assumiamo inoltre che quanto
Testa è uguale al valore -1 allora la pila è vuota, mentre se Testa è uguale al valore
MAX-1 il vettore è saturo: la pila non può ospitare alcun elemento.
Testa = n-1
0
a1
1
a2
2
a3
n-1
...
...
MAX -1
an
Figura 4.2 – Pila di ‘n’ elementi rappresentata con vettori
Operatore POP
Diamo luogo alla operazione di estrazione, restituendo il valore memorizzato
all’interno della cella indicata dalla variabile Testa, V[Testa], e decrementando di una
unità la variabile Testa.
Operatore PUSH
Diamo altresì luogo alla operazione di inserimento, incrementando prima il valore
della variabile Testa e memorizzando all’interno della cella indicata dalla variabile
incrementata, V[Testa], il valore da inserire.
Le operazioni di inserimento ed estrazione richiedono tempo costante. I numeri di passi
da eseguire per implementare i due operatori è indipendente dal numero di elementi
presenti all’interno della pila.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 12/18
4.2 Implementazione tramite puntatori
La struttura dati lista concatenata è un’alternativa alla rappresentazione sopra
descritta. La variabile di tipo puntatore, denominata Testa, rappresenta l’ultimo
elemento della pila. In tal caso la condizione di pila vuota è data da: Testa = NULL.
Similmente
alla
rappresentazione
mediante
utilizzo
dei
vettori,
questa
implementazione è caratterizzata da tempi costanti sia per l'operazione di inserimento
che per l’operazione di estrazione.
Inserimento
Diamo luogo all'operazione di inserimento modificando la lista come di seguito
raffigurato:
Testa
a1
a2
a3
a4
NULL
PUSH
Testa
Testa
Nuova
chiave
a1
a2
a3
a4
NULL
Creiamo un nuovo record, poniamo il campo successivo uguale al valore assunto da
Testa ed infine spostiamo Testa verso il nuovo elemento creato.
Inserimento Pila – Lista concatenata
Push( elemento e)
{
Record r
alloca un nuovo record, r
poni il campo valore di r uguale ad e, rÆvalore=e
poni il campo successivo di r uguale a Testa, rÆsuccessivo=Testa
poni Testa uguale ad r, Testa=r
}
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
_
Liste Pile e Code
Pag. 13/18
Estrazione
Diamo luogo all’operazione di estrazione modificando la lista come di seguito
raffigurato:
Creiamo un nuovo record, poniamo il campo successivo uguale al valore assunto da
Testa ed infine spostiamo Testa verso il nuovo elemento creato.
Estrazione Pila – Lista concatenata
Pop()Æelemento
{
Puntatore p
p = Testa
Testa = TestaÆSuccessivo
Dealloca p
}
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
_
Liste Pile e Code
Pag. 14/18
5. Il tipo di dato astratto Coda
Una coda è una collezione di elementi che ammette esclusivamente due operazioni:
•
l'inserimento di un elemento ad una estremità, detta fine
•
l'estrazione dell'elemento presente nell'altra estremità, detta inizio.
Estrazione
Inserimento
Inizio
a1
Fine
a2
a3
...
an
Figura 5.1 – Coda
Denominiamo la politica di gestione degli elementi di una coda con l’acronimo FIFO,
First In First Out.
Tipo di dato: Coda
Sequenza Q di ‘n’ elementi ( al, ...,an ) estratti da un insieme S
Operazioni: IsEmpty()Æresult
Restituisce true se Q è vuota altrimenti restituisce false
Inserisci( elemento e )
Inserisce l’elemento ’e’ alla fine di Q
Estrazione()Æelemento
Restituisce l’elemento posto all’inizio di Q
5.1 Implementazione tramite vettore circolare
Una coda è realizzabile attraverso l’utilizzo della struttura dati vettore circolare.
Quest’ultimo rappresenta un tradizionale vettore di ampiezza prefissata, la cui
caratteristica di circolarità è data dalla regola di incremento utilizzata per la scansione
delle celle. Si da luogo a tale operazione utilizzando una variabile intera, index,
incrementata secondo la formula:
index = (index + 1) mod MAX, dove MAX è la dimensione del vettore.
In tal modo index assume valori compresi tra zero e MAX-1.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 15/18
Rappresentiamo la coda mediante vettore circolare definendo un vettore V di
ampiezza MAX e due contatori, inizio e fine, che incrementiamo modulo MAX.
Inizio farà riferimento alla cella del vettore che precede il primo elemento, fine farà
riferimento alla cella del vettore che contiene l’ultimo elemento.
Inizio = (Inizio+1)mod MAX
Inizio
Fine
Fine = (Fine+1)mod MAX
0
a1
a1
1
2
...
an
MAX-1
Figura 5.2 – Coda rappresentata con vettore circolare
Possiamo pertanto definire quattro stati della coda:
1. Stato iniziale:
inizio = fine = 0
2. Coda vuota:
inizio = fine
3. Coda non vuota: inizio ≠ fine
4. Coda piena:
(fine + 1 ) mod n = inizio
Nota
Una delle particolarità di questa rappresentazione è data dal fatto che non si avrà mai un riempimento
fisico del vettore che si potrà riempire fino ad un massimo di MAX-1 elementi. Questo perché se
inserissimo un elemento anche nella unica posizione che eventualmente rimarrebbe vuota non saremmo
più in grado di distinguere quando la coda è piena e quando la coda è vuota.
Implementiamo gli operatori di inserimento ed estrazione.
Inserimento
Diamo luogo all’operazione di inserimento incrementando la variabile fine e
memorizzando nella cella del vettore indicata da fine il valore da inserire.
L’operazione di inserimento ha un tempo di esecuzione costante.
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 16/18
_
Inserimento Coda – Vettore Circolare
Inserisci( elemento e)
{
Vettore V
se ((fine+1) mod MAX) == inizio allora
coda piena
altrimenti
fine = ( fine + 1 ) mod MAX
V[fine] = e
}
Estrazione
Diamo luogo all’operazione di estrazione incrementando la variabile inizio e
ritornando il valore memorizzato nella cella del vettore indicata da inizio.
L’operazione di estrazione ha un tempo di esecuzione costante.
_
Estrazione Coda – Vettore Circolare
Estrazione()ÆElemento
Vettore V
se inizio = fine allora
coda è vuota
altrimenti
inizio = ( inizio + 1 ) mod MAX
ritorna V[inizio]
5.2 Implementazione tramite lista
L’utilizzo della struttura dati lista concatenata richiede l’utilizzo di due variabili di
tipo puntatore:inizio e fine. Esse faranno riferimento rispettivamente al primo ed
all'ultimo elemento della coda.
Inizio
Fine
a1
a2
...
an
null
Figura 5.5.- Coda realizzata con lista concatenata
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Liste Pile e Code
Pag. 17/18
I quattro stati della coda sono pertanto:
1. Stato iniziale:
inizio = fine = NULL
2. Coda vuota:
inizio = NULL
3. Coda non vuota: inizio ≠ fine
4. Coda piena:
Non applicabile, la struttura dati è dinamica
Inserimento
Diamo luogo all’operazione di inserimento allocando un nuovo record, linkando
quest’ultimo al record puntato da fine ed infine spostando il puntatore fine verso
l’ultimo elemento inserito.
L’operazione di inserimento ha un tempo di esecuzione costante.
Inserimento Coda – Lista concatenata
_
Inserisci(Elemento e)
{
Puntatore p
p = alloca record
pÆchiave = e
pÆnext = NULL
fineÆnext = p
fine = p
}
Estrazione
Diamo luogo all’operazione di estrazione spostando il puntatore inizio e deallocando il
record precedentemente puntato da inizio e contenente quindi il valore da estrarre.
Estrazione Coda – Lista concatenata
Estrazione()ÆElemento
{
Puntatore p
Elemento e
se inizio = NULL allora
coda è vuota
altrimenti
p = inizio
inizio = inizioÆnext
e = pÆchiave
dealloca p
ritorna e
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
_
Liste Pile e Code
Pag. 18/18
}
Appunti Laboratorio di Algoritmi e Strutture Dati – Prof. Maurizio GIACCI
Fly UP