Strutture dati elementari - PowerPoint PPT Presentation

About This Presentation
Title:

Strutture dati elementari

Description:

Title: PowerPoint Presentation Last modified by: stefano berardi Document presentation format: Presentazione su schermo (4:3) Other titles: Times New Roman Arial ... – PowerPoint PPT presentation

Number of Views:72
Avg rating:3.0/5.0
Slides: 54
Provided by: unito152
Category:

less

Transcript and Presenter's Notes

Title: Strutture dati elementari


1
Strutture dati elementari
  • Parte 6
  • Vettori e ricerca binaria
  • Matrici e triangolo di Tartaglia
  • Records (cenni)

Corso A Prof. Stefano Berardi
http//www.di.unito.it/stefano Corso B Prof.
Ugo de Liguoro http//www.di.unito.it/deligu

2
Un quadrato magico di numeri
Albert Durer. Melencolia,1514 (dettaglio)
16 3 2 13
5 10 11 8
9 6 7 12
4 15 14 1
3
Indice Parte 6 i Vettori
  1. Strutture dati i vettori.
  2. Esempi elementari stampa, somma, test di
    uguaglianza, inversione per vettori.
  3. Esempi più complessi ricerca lineare e binaria
    per vettori.
  4. Matrici il triangolo di Tartaglia.
  5. Records e vettori parzialmente riempiti (cenni).

4
1. Strutture dati I Vettori
  • In generale, le strutture dati sono un modo per
    rappresentare insiemi di informazioni nella
    memoria di un computer
  • Una semplice variabile è un caso banale di una
    struttura dati, un modo per rappresentate una
    singola informazione.
  • In questa parte del corso studieremo strutture
    dati per rappresentare insiemi di dati di tipo
    omogeneo, i vettori e le matrici, e per
    rappresentare dati di tipo eterogeneo, i record.

5
Vettori (o array) in C
  • Un vettore v (array) è una sequenza di n oggetti
    tutti del medesimo tipo, detti elementi del
    vettore, per qualche ngt0 detto la dimensione del
    vettore.
  • Gli elementi del vettore sono indicizzati
    utilizzando interi positivi, da 0 fino a n-1,
    immaginati disposti come segue
  • v0, v1, , vn-1

6
Notazioni per i vettori
  • Le notazioni che seguono NON sono codice C/C,
    ma sono la notazione che usiamo quando parliamo
    dei vettori
  • i..j k?Z i ? k ? j intervallo di interi
  • vi..j vi, vi1, , vj
  • Quindi, se i gt j allora i..j lista vuota se un
    vettore v ha n elementi questi formano linsieme
    v0..n-1.

7
I vettori nella memoria RAM
  • Gli elementi di un vettore occupano spazi di
    memoria consecutivi nella memoria RAM della
    macchina di Von Neumann. Lindirizzo vi di
    ogni elemento si calcola dunque con la formula
    vi v0 id, dove d sizeof (tipo di
    v)

d dimensione di ogni elemento (es. 4 bytes)
vi indirizzo vi bid
v0 ind. b
v1 ind. bd
v2 ind. b2d
v
b indirizzo base v0 indirizzo v0
(es.byte n. 1 milione)
i indice di vi (detto anche spiazzamento ).
Es. i100, indirizzo di vi 1 000 400.
8
Vettori dichiarazione
  • Un vettore v in C è una costante di tipo
    indirizzo. Viene identificato con v0,
    lindirizzo del suo primo elemento. La
    dichiarazione di v consiste nellindicazione del
    nome e del tipo degli elementi del vettore, e
    nellindicazione del loro numero, detto la
    dimensione del vettore. Con sizeof(v) si indica
    invece il numero di bytes occupati dal vettore

int v100
9
Vettori lerrore più comune
  • La dimensione di un vettore deve essere una
    costante o una variabile già assegnata.
  • Quindi le seguenti righe sono errate (ma
    purtroppo il compilatore non lo segnala!)

int dim double wdim / ERRORE la variabile
dim, per il solo fatto di essere dichiarata, ha
un valore, ma non si può prevedere quale. Quindi
si crea un vettore di dimensione casuale, a
volte di miliardi di elementi, producendo in
questultimo caso un crash di programma /
10
Accesso agli elementi di un vettore
  • Per accedere ai singoli elementi di un vettore si
    usano gli indici attenti a non confondere gli
    indici con la dimensione che compare nella
    dichiarazione.

Gli interi tra hanno diverso significato in
una dichiarazione e in una assegnazione nella
dichiarazione, 100 è la dimensione,
nellassegnazione, 0 è un indice
int v100 / dichiarazione / v0 6 /
assegnazione, accesso in scrittura / int n
v0 / assegnazione, accesso in lettura /
11
Inizializzazione attraverso un ciclo
  • Il programma che segue inizializza a 0 tutti gli
    elementi del vettore v

int v100 for (int i 0 i lt 100 i) vi
0
Prima dellinizializzazione, tutti gli elementi
di v, avendo un indirizzo di memoria, hanno
comunque un valore, ma un valore casuale
provate a stamparli.
12
Inizializzazione attraverso una dichiazione
  • Il C consente di definire (quindi anche di
    inizializzare) un vettore elencandone gli
    elementi. Non è necessario indicare il numero
    degli elementi, che viene calcolato

double a 22.2, 44.4, 66.6 // alloca un
vettore a di 3 float con // a0 22.2, a1
44.4, a2 66.6
13
Attenti a non uscire dai limiti di un vettore
  • Se un vettore ha dimensione d ed ilt0, oppure i ?
    d, allora vi non dovrebbe essere definito.
  • Invece, in C/C, vi esiste sempre, è per
    definizione il contenuto dellindirizzo di
    memoria calcolato dalla formula
  • v0 i sizeof (tipo di v)
  • ossia un valore a caso!! Questa convenzione è
    introdotta per semplicità di calcolo. Il
    compilatore non ci avvisa quando utilizziamo un
    vi con ilt0, oppure i ? d, sta a noi evitare che
    accada.

14
2. Alcuni semplici esempi stampa di un vettore
  • La stampa di un vettore deve avvenire elemento
    per elemento (dunque usando ad es. un ciclo for)

int v100 for (int i 0 i lt 100 i)
cout ltlt vi
15
Somma e media di un array di numeri
Anche la somma di un vettore deve avvenire
elemento per elemento (dunque usando ad es. un
ciclo for)
double v100 double somma 0 for (int i 0
i lt 100 i) somma somma
vi double media somma/100.0 /dividiamo per
un numero reale per evitare larrotondamento/
16
Un test di uguaglianza errato
  • Se applichiamo il test a due vettori distinti
    otteniamo risposta costantemente uguale a false

int v100, w100 if (v w) / v, w sono
identificati con gli indirizzi dei loro primi
elementi, dunque il test confronta questi
ultimi. Dato che v, w sono allocati in
posizioni diverse della memoria, i loro primi
elementi hanno diversi indirizzi, anche quando v,
w hanno elementi di valore uguale. Dunque vw
vale sempre false. /
17
Un test di uguaglianza corretto
  • Per decidere se due array di egual tipo e egual
    dimensione N hanno valori uguali per indici
    uguali, si devono confrontare tutti gli elementi
    usando uniterazione. Ecco una soluzione con un
    WHILE

/ Il ciclo while trasporta il contatore i al
primo indice per cui vi!wi, se ne esiste
uno, altrimenti trasporta i fino ad N / int i
0 while (i lt N vi wi) i if (i N)
cout ltlt "v,w hanno elementi uguali per indici
uguali" else / i lt N / cout ltlt "v, w hanno
diverso lelemento di posto" ltlt i
18
Un test di uguaglianza corretto 2
  • Per decidere se due array di egual tipo e egual
    dimensione N hanno valori uguali per indici
    uguali, si devono confrontare tutti gli elementi
    usando uniterazione. Ecco la traduzione della
    soluzione precedente in un ciclo FOR

int i for (i0 i lt N vi wi i)
//il corpo del FOR e vuoto if (i N) cout ltlt
"v,w hanno uguali per indici uguali" else cout
ltlt "v, w hanno diverso lelemento di posto" ltlt i
19
Passaggio di un vettore ad una funzione
  • Un array viene passato alle funzioni sempre per
    indirizzo. Attenzione nella dichiarazione del
    parametro scriviamo int a, nella chiamata non
    ripetiamo il tipo e scriviamo solo a

int sum( int a, int n) / prec.0ltnltdim.a
post.condsum(a,n) somma di a0..n-1/ int
i,s for (i0,s0 iltn i) ssai return
s int main() int a 11, 33, 55, 77 int
size sizeof(a)/sizeof(int) //num.el. di a cout
ltlt"sum(a,size) "ltltsum(a,size)ltltendl
20
Modifica di un vettore
  • Poiché un vettore viene passato per riferimento
    (indirizzo) se una chiamata di funzione modifica
    gli elementi del vettore, queste modifiche sono
    permanenti

void scambia (int v, int i, int j) // pre 0 ?
i, j lt dimensione di v // post scambia vi con
vj int temp vi vi vj vj
temp
21
Inversione di un vettore v
  • Idea. Poniamo due indici i, j agli estremi di v e
    muoviamo i, j uno verso laltro. Scambiamo tra
    loro gli elementi di posto i e j, fino a che
    tutti gli elementi alla sinistra di v sono
    scambiati con tutti gli elementi alla destra di v.

void inverti (int v, int n) / PRE 0 ? n ?
dimensione di v. POST elementi v0..n-1 in
ordine inverso / for (int i 0, int jn-1
iltj i,j--) scambia(v, i, j)
i0
i1
jn-2
jn-1
22
3. Ricerca Lineare e Binaria
  • Si vuole decidere se un intero n appartiene alla
    sequenza rappresentata dal vettore v. Il metodo
    più semplice è la ricerca lineare confrontiamo n
    con v0, v1, v2, in questordine.

bool Member (int n, int v, int dim) / pre la
dimensione di v e gt dim gt0. post restituiamo
true ? esiste i tale che vin / bool trovato
false / trovato indica se n e gia stato
trovato. trovato puo cambiare solo da false a
true, mai viceversa./ for(int i0iltdimi)if(v
in)trovatotrue return trovato
23
Ricerca lineare con interruzione uso di una
variabile flag
  • Per efficienza, potremmo voler interrompere la
    ricerca di n non appena troviamo n. A tal fine,
    definiamo un valore booleano trovato che parte
    da vero, e non appena trovato vale vero
    usciamo. Il test del ciclo quindi deve essere
    continuiamo se !trovato è vero. Una variabile
    booleana che ci avvisa quando uscire da un ciclo
    è detta una flag.

bool Member (int n, int v, int dim) / pre la
dim. di v e gt dim gt0. post true sse esiste i
t.c. vi n/ bool trovato false //detta
variabile flag for (int i0 iltdim !trovato
true i) if (vi n) trovato true
return trovato
24
Ricerca lineare con interruzione seconda
soluzione
  • Possiamo eliminare la flag trovato, spostando
    (la negazione di) vi n nel test del for. In
    tal caso, dobbiamo dichiarare i fuori dal for

bool Member (int n, int v, int dim) // pre la
dimensione di v e gt dim gt 0 // post true sse
esiste i t.c. vi n int i for (i0 i
lt dim vi ! n i) return (i lt dim)
25
Ricerca binaria
  • Se un vettore (per es. di interi) è ordinato in
    senso crescente, la ricerca di un elemento nel
    vettore può essere enormemente accelerata
    sfruttando il metodo della Ricerca Binaria
  • Manteniamo due indici, i e j, a delimitazione
    della porzione vij di v in cui cercare
  • Ad ogni passo confrontiamo n con il valore di
    indice medio in i..j, cioè m(ij)/2
  • Se vm ! n allora cerchiamo in vi..m-1 o in
    vm1..i a seconda che n lt vm oppure vm lt n.
  • Daremo ora una descrizione dettagliata del
    funzionamento della ricerca binaria, in 5 tappe.

26
Ricerca binaria 1 pre- e post-condizioni
  • 1. Definiamo il problema. Input il valore
    cercato in v è n. Pre-condizione il vettore v
    di dimensione dim è ordinato.
  • Post-condizione restituire lindice del valore
    cercato (un i tale che vin, se esiste),
    altrimenti la lunghezza dim di v (dim sta per
    non trovato)

0
dim-119
0
dim-119
Valore cercato n 25. Indice del valore
cercato 7 ( v725)
27
Ricerca binaria 2 una proprietà invariante
  • 2. Individuiamo una proprietà invariante della
    ricerca binaria, cioè una proprietà significativa
    che resta vera durante tutta la durata della
    ricerca binaria
  • Propr. Invariante durante la ricerca binaria
    considero solo dei segmenti vij di v tali che
    se n è in v, allora n è in vij.

vij
indice 0
indice 19
i3
j16
Cerco n25 in vij
Per es. se n25 è in v, allora n è in vi..j
v3..16.
28
Ricerca binaria 3 il funzionamento
  • 3. La ricerca binaria cerca un modo per
    avvicinarsi alla soluzione mantenendo vero
    linvariante
  • Passo generico dividiamo il sottovettore in due
    parti (quasi) uguali. Caso 1. Se n si trova nel
    punto intermedio restituisco m

Spazio dove avviene la ricerca di n
0
dim-1 19
Se il valore cercato è n 43 allora lho trovato
Punto intermedio m di ij
29
Ricerca binaria 3 il funzionamento
  • 3. La ricerca binaria cerca un modo per
    avvicinarsi alla soluzione mantenendo vero
    linvariante
  • Passo generico dividiamo il sottovettore in due
    parti (quasi) uguali. Caso 2. Se il valore n
    cercato è lt di quello nel punto intermedio,
    allora, dato che il vettore è ordinato, n si
    trova nella parte sinistra di vij.

Spazio di ricerca
0
19
Punto intermedio m di i..j
Valore cercato n 25
30
Ricerca binaria 3 il funzionamento
  • 3. La ricerca binaria cerca un modo per
    avvicinarsi alla soluzione mantenendo vero
    linvariante
  • Passo generico dividiamo il sottovettore in due
    parti (quasi) uguali. Caso 3. Se il valore
    cercato n è gt di quello nel punto intermedio,
    allora n si trova nella parte destra di vij .

Spazio di ricerca
0
19
Punto intermedio m di i..j
Valore cercato n 60
31
Ricerca binaria 4 la fine della computazione
  • 4. Definiamo in quale momento la computazione
    si deve fermare
  • Quando si sia trovato il valore nel punto
    intermedio di indice m, oppure .

Spazio di ricerca
0
19
Valore cercato (e trovato) n 43
Punto intermedio di indice m
32
Ricerca binaria 4 la fine della computazione
  • 4. Definiamo in quale momento la computazione
    si deve fermare
  • . oppure quando il sottovettore cui limitiamo la
    ricerca sia ridotto al vettore vuoto (cioe al
    vettore vij con igtj)

0
19
n23 dovrebbe essere qui in mezzo, tra 21 e 25
ma questo intervallo è vuoto. Il valore n non
viene trovato.
Valore cercato (e non trovato) n23
33
Ricerca binaria 5linizio della computazione
  • 5. Definiamo le condizioni iniziali per la
    ricerca binaria
  • Allinizio, il segmento vij di vettore in cui
    cercare n e lintero vettore n.

Spazio di ricerca iniziale v0..n-1
0
19
34
Ricerca binaria i dettagli della codifica dei
dati
  • Stabiliamo ora i dettagli della codifica del
    segmento di vettore Vi..j che usiamo durante la
    ricerca.
  • Il sottovettore Vi..j, a cui limitiamo la
    ricerca, è compreso tra le posizioni i e j incluse

Spazio di ricerca in un passo generico della
computazione
m
i
j
0
19
  • Il punto medio m ha indice (i j) diviso 2
  • Se i gt j allora il sottovettore Vi..j è vuoto

35
Ricerca binaria limplementazione
  • Scriviamo ora una funzione C che implementa la
    ricerca binaria, usando pre- e post-condizioni e
    linvariante come commenti. La funzione ottenuta
    è decisamente breve rispetto a tutta la
    discussione che è servita a presentarla.

36
Ricerca binaria limplementazione
int binsearch (int n, int v, int dim) // pre
la dim. di v e gt dim e v0..dim-1 è ordinato
// post i tale che vi n se ne esiste uno,
altrimenti dim int i 0, j dim - 1, m
while (i lt j) //inv. se n in v0..n-1 allora n
in Vi..j m (ij)/2 if (vm n)
return m else if (n lt vm) j m - 1
else / (vm lt n) / i m 1 return
dim // dim sta per non trovato
37
4. Matrici in C
  • Una matrice a due dimensioni viene vista come un
    vettore bidimensionale, o di vettori, ognuno dei
    quali rappresenta una riga della matrice la
    dichiarazione di una matrice è simile a quella
    vettore, eccetto che richiede due dimensioni
  • double A1020
  • // matrice 10righe x 20colonne di double
  • Aij 7.23
  • // se 0 ? i lt 10 e 0 ? j lt 20 allora
  • // scrive 7.23 come valore della
  • // i-esima riga e j-esima colonna di A

38
Passaggio di un vettore bidimensionale a funzione
  • Nei parametri formali di una funzione un vettore
    V di dimensione 1 può figurare come
  • void f(int V, int n)
  • senza lindicazione della lunghezza di V dentro
    int V la lunghezza n del vettore è a sua volta
    un parametro, che può cambiare da una chiamata
    allaltra.
  • Nel caso di una matrice A di due o più
    dimensioni, invece, le dimensioni debbono essere
    costanti indicate dentro A stesso, come segue

void g(double A 1020 )
39
Passaggio di un vettore bidimensionale a funzione
  • Questo tipo di dichiarazione è molto scomoda
    una funzione g che stampa una matrice A di 10x20
    elementi non può essere utilizzata per stampare
    una matrice B di 20x20 elementi, perchè double
    A1020 e double B2020 sono due tipi
    diversi.
  • La seconda dimensione di A, ovvero il numero
    20 delle colonne di A, è purtroppo necessaria per
    ricostruire lindirizzo della variabile Aij
    nella macchina di Von Neumann, e non è
    ricostruibile a partire dalla sola variabile A.

void g(double A 1020 )
40
Un esempio di uso di matriciil triangolo di
Tartaglia
Scriveremo ora una funzione che assegna le prime
n1 righe del triangolo diTartaglia a una matrice
(n1)x(n1). Per definizione, ogni elemento posto
ai lati del triangolo di Tartaglia vale 1, e ogni
altro elemento è la somma dellelemento posto
sopra e di quello posto sopra e a destra.
Niccolò Tartaglia 1499-1557
41
Un esempio di uso di matriciil triangolo di
Tartaglia
0 1 2 3 4 5
6
Righe da 0 a 6 del Triangolo in una matrice A
di 7x7. Per definizione 1 A00 A1,0
A20 Ai,0 e 1 A00 A1,1
A22 Ai,i e per 0ltjlti Aij
Ai-1j-1Ai-1j.







1 1 1 1 2 1 1 3
3 1 1 4 6 4 1 1
5 10 10 5 1 1 6 15
20 15 6 1
0 1 2 3 4
5 6
Per es. 6 A42 A31 A32 3 3.
42
Il triangolo di Tartaglia usando una matrice
void Tartaglia(int n) // stampa righe da 0 a n
del triangolo di Tartaglia int an1n1
//definisce matrice (n1)x(n1) for (int i 0
iltn i) // costruzione riga per riga
ai0 1 // assegna 1 a tutta la colonna 0
for (int j 1 jlti j) // assegna colonne da
1 a i-1 aij ai-1j-1
ai-1j aii 1 // assegna 1 a tutta
la diagonale // stampa righe da 0 a n del
triangolo (vedi pagina seguente per sapere
come)
43
Stampa del triangolo di Tartaglia
void Tartaglia(int n) // definisce righe da 0 a
n (vedi pagina precedente per sapere come)
// stampa righe da 0 a n for (int i 0 i
lt n i) for (int j 0 j lt i
j) cout ltlt setw(4) ltlt aij
cout ltlt endl Listruzione setw(4)
esegue la prossima stampa con almeno 4 spazi.
Richiede di includere la libreria include
ltiomanip.hgt
44
5. I records (cenni)
Un record è una tupla di valori di tipo
possibilmente diverso (è questa la differenza con
i vettori) a cui accediamo attraverso etichette
anziché indici
struct ltnome strutturagt lttipo1gt ltetichetta
campo1gt ... lttipokgt ltetichetta campokgt
Come concetto matematico, un record corrisponde a
un prodotto cartesiano tipo1 x x tipon mentre
un vettore corrisponde a un insieme potenza
tipon
45
I record nella memoria
  • I record nella memoria di una macchina di Von
    Neumann sono rappresentati con celle adiacenti,
    ma di diversa dimensione. E necessario
    individuare ogni cella assegnadole un nome

Nome_1

Nome_k
Possiamo rappresentare i razionali come
frazioni, e le frazioni come un record di due
campi numeratore e denominatore. Si tratta solo
di un esempio i razionali non sono una struttura
dati abbastanza complessa da giustificare luso
dei records.
46
Frazioni rappresentate da un record
struct Ratio int num int den
Se r e un record che rappresenta una frazione,
indichiamo numeratore e denominatore di r con
r.num e r.den
47
Record come valori di funzioni
  • Diversamente dagli array, le struct in C sono
    passate (e restituite) per valore dunque ogni
    chiamata costruisce una copia del record e la
    assegna al parametro formale della funzione

Ratio NewRatio(int n, int d) / Pre-cond.
d!0 Post-cond.NewRatio(n,d) restituisce il
record che rappresenta n/d / Ratio r
r.num n r.den d return r int
main() Ratio a NewRatio(2,3) // a2/3
48
Record come valori di funzioni
  • Un altro esempio, la somma di frazioni. Le
    strutture in C sono passate per valore, creando
    delle copie dei valori passati.

Ratio SumRatio(Ratio a, Ratio b) // post
restituisce il razionale a b Ratio r r.num
a.num b.den a.den a.num r.den a.den
b.den return r int main() Ratio a, b
Ratio c SumRatio(a,b) // c a b
49
Vettori parzialmente riempiti
  • Di un vettore occorre ricordare la
    dimensione se poi se ne usa solo una parte, come
    abbiamo fatto nellesercizio sui numeri primi,
    bisogna sapere sin dove è riempito, ovvero,
    quale è il prossimo indirizzo libero.

v
prox_libero
Se non ci sono indirizzi liberi, il prossimo
indirizzo libero per definizione è la dimensione
dim del vettore v.
50
Vettori parzialmente riempiti come record
  • Possiamo definire un record per rappresentare
    vettori parzialmente riempiti. Usiamo un record
    con due campi un vettore e il primo indirizzo
    ancora libero del vettore (se esiste)

struct Array int v10
int prox_libero / prox_libero indice
del prossimo indirizzo libero /

51
Funzioni su vettori parzialmente riempiti
Un esempio di una funzione che agisce sui
vettori parzialmente riempiti la stampa di tutti
gli elementi del vettore effettivamente in uso
(dunque fino alla prossima posizione libera
esclusa).
void Mostra(Array a) /post stampa la parte
riempita di v, e cioé (a.v)0..a.prox_libera-1
/ for (int i 0 i lt a.prox_libero i)
cout ltlt a.vi ltlt " " cout ltlt
endl
52
Funzioni su vettori parzialmente riempiti
Un altro esempio di una funzione che agisce
sui vettori parzialmente riempiti come
aggiungere un elemento a quelli effettivamente in
uso
bool Aggiungi(int n, Array a) / post se
a.prox_libera lt 10, aggiunge n in ultima pos. e
restituisce true restituisce false altrimenti
/ if (a.prox_libero lt 10) a.va.prox_libero
n a.prox_libero return true else
return false
53
Riepilogo
  • Vi sono due strutture dati predefinite per
    rappresentare collezioni finite di valori
    vettori (array) e record (struct)
  • I vettori hanno dimensione fissa, elementi
    omogenei, e sono passati per riferimento
  • Vettori a due dimensioni rappresentano una
    matrice.
  • I record hanno un numero fisso di campi
    individuati da nomi, hanno tipi eventualmente
    diversi di valori, e sono passati per valore.
Write a Comment
User Comments (0)
About PowerShow.com