grafi: rappresentazioni e visite - unina.itwpage.unina.it/benerece/lasd-2016/6-1 grafi + esercizio...
TRANSCRIPT
Grafi: Rappresentazioni e Visite
Laboratorio di Algoritmi e Strutture Dati
Massimo Benerecetti
Ci sono due tipi di rappresentazione standard
per grafi in un computer:
• Rappresentazione a matrice di adiacenza
• Rappresentazione a liste di adiacenza
Rappresentazone di grafi
A
BC
F
DE
A B C D E F
A
B
C
D
E
F
0 1 0 1 0 0
0 0 1 0 0 0
0 0 0 0 1 0
1 0 1 0 0 0
0 0 0 1 0 0
0 0 0 0 0 0
Rappresentazone di grafi orientati
Rappresentazione a matrice di adiacenza questa
volta per rapresentare un grafo orientato.
Spazio: |V|2
A
BC
F
DE
A
B
C
D
E
F
B D
A
D
E
C
Rappresentazone di grafi orientati
Rappresentazione a liste di adiacenza questa volta
per rapresentare un grafo orientato.
C
Rappresentazione a liste di adiacenza questa volta
per rapresentare un grafo orientato.
A
BC
F
DE
A
B
C
D
E
F
B D
D
E
C
Spazio: a |V| + b |E|
a b
Rappresentazone di grafi orientati
A C
Rappresentazone di grafi
• Matrice di adiacenza
• Spazio richiesto O(|V|2)
• Verificare se i vertici u e v sono adiacenti richiede tempo O(1).
• Molti 0 nel caso di grafi sparsi
• Liste di adiacenza
• Spazio richiesto O(|E|+|V|)
• Verificare se i vertici u e v sono adiacenti richiede tempo O(|V|).
Visita in ampiezza BFS
• Tecnica di visita di un grafo
• È una variazione della visita in ampiezza per alberi binari
• Viene mantenuta una coda di vertici da visitare, inizialmentecontenete solo la sorgente della visita;
• La visita di s procede come segue:
1. Ad ogni iterazione viene prelevato un vertice s dalla coda;
2. Si accodano tutti i vertici adiacenti (scoperti) ad s;
3. Si termina la visita del vertice s e si ritorna al passo 1 per procedere con l’iterazione successiva.
• La visita termina quando la coda è vuota.
• Bisogna evitare di rivisitare vertici già visitati
• Attenzione alla presenza di cicli.
Algoritmo BFS
• Per distinguere tra i vertici non visitati, quelli sciperti, equelli visitati coloriamo:• ogni vertice scoperto di grigio
• ogni vertice non scoperto di bianco
• ogni vertice visitato di nero
• Vengono accodati solo i vertici che non sono ancora statiscoperti (cioè bianchi)
• I vertici in coda saranno i vertici scoperti e non ancoravisitati (cioè grigi)
• I vertici già scoperti o visitati non vengono più riconsiderati.
Algoritmo BFSBSF(G:grafo, s:vertice)for each vertice u V(G) - {s}
do colore[u] = Bianco
pred[u] = Nil
colore[s] = Grigio
pred[s] = Nil
Coda = {s}
while Coda
do u = Testa[Coda]
for each v Adiac(u)
do if colore[v] = Bianco
then colore[v] = Grigio
pred[v] = u
Accoda(Coda,v)
Decoda(Coda)
colore[u] = Nero
Inizializzazione
Accodamento dei soli
nodi non visitati
Algoritmo BFS: complessitàBSF(G:grafo, s:vertice)for each vertice u V(G) - {s}
do colore[u] = Bianco
pred[u] = Nil
colore[s] = Grigio
pred[s] = Nil
Coda = {s}
while Coda
do u = Testa[Coda]
for each v Adiac(u)
do if colore[v] = Bianco
then colore[v] = Grigio
pred[v] = u
Accoda(Coda,v)
Decoda(Coda)
colore[u] = Nero
O(|V|)
O(|Eu|)Eu = lunghezza della
lista di adiacenza
di u
BSF(G:grafo, s:vertice)for each vertice u V(G) - {s}
do colore[u] = Bianco
pred[u] = Nil
colore[s] = Grigio
pred[s] = Nil
Coda = {s}
while Coda
do u = Testa[Coda]
for each v Adiac(u)
do if colore[v] = Bianco
then colore[v] = Grigio
pred[v] = u
Accoda(Coda,v)
Decoda(Coda)
colore[u] = Nero
O(|V|)
O(|E|)E = dimensione
delle liste di
adiacenza.
Numero di archi
Algoritmo BFS: complessità
Algoritmo BFS: complessità
L’algoritmo di visita in Breadth-First impiega tempoproporzionale alla somma del numero di vertici e delnumero di archi (dimensione delle liste di adiacenza).
T(V,E) = O(|V|+|E|)
Stampa del percorso minimo
Percorso-minimo(G:grafo,s,v:vertice)
BFS(G,s,pred[])
Stampa-percorso(G,s,v,pred)
Stampa-percorso(G:grafo,s,v:vertice,pred[]:array)
if v = s
then stampa s
else if pred[v] = NIL
then stampa “non esiste alcun cammino tra
s e v”
else
Stampa-percorso(G,s,pred[v],pred)
print v
Visita in Profondità (DFS)• Tecnica di visita di un grafo
• È una variazione della visita in profondità per alberi binari
• La visita di s procede come segue:• Si visitano ricorsivamente tutti i vertici adiacenti ad s;
• Si termina la visita del vertice s e si ritorna.
• Bisogna evitare di rivisitare vertici già visitati• Bisogna anche qui evitare i cicli
• Nuovamente, quando un vertice è stato scoperto e (poi)visitato viene marcato opportunamente (colorandolo)
Algoritmo DFS
Manterremo traccia del momento (tempo) in cui
ogni vertice v viene scoperto e del momento in
cui viene visitato (o terminato).
Useremo inoltre due array d[v] e f[v] che
registrano il momento in cui v verrà scoperto e
quello in cui verrà visitato.
La variabile globale tempo serve a registrare il
passaggio del tempo.
Il tempo viene usato per studiare le proprietà di DFS
DFS: intuizioniI passi dell’algoritmo DFS
si parte da un vertice non visitato s e lo si visita
si sceglie un vertice non scoperto adiacente ad s.
da s si attraversa quindi un percorso di vertici adiacenti(visitandoli) finché possibile (DFS-Visita):
• cioè finché non si incontra un vertice già scoperto/visitsto
appena si resta “bloccati” (tutti gli archi da un verticesono stati scoperti), si torna indietro (backtracking) di unpasso (vertice) nel percorso attraversato (aggiornando ilvertice s al vertice corrente dopo il passo all’indietro).
si ripete il processo ripartendo dal passo.
DFS: DFS-Visita
DFS-Visita: algoritmo principale della DFS
sia dato un vertice u di colore bianco in ingresso
visitare il vertice u: colorare u di grigio e assegnare il
tempo di inizio visita d[u]
visitare in DFS ricorsivamente ogni vertice bianco
adiacente ad u con DFS-Visita
colorare di nero u e assegnare il tempo di fine visita
f[u].Chiamata ricorsiva
ba
c
e f
d
b
ac
e
f
d
b acef d
ba
c
e f
d
Albero di copertura Depth-first
Archi dell’alberoArchi di ritorno
Algoritmo DFSDSF(G:grafo)
for each vertice u V
do colore[u] = Bianco
pred[u] = NIL
tempo = 0
DSF-Visita(G:grafo,u:vertice)
colore[u] = Grigio
d[u] = tempo = tempo + 1
for each vertice v Adiac[u]
do if colore[v] = Bianco
then pred[v] = u
DFS-Visit(G,v)
colore[u] = Nero
f[u] = tempo = tempo + 1
Abbreviazione per:tempo=tempo+1
d[u]=tempo
Abbreviazione per:tempo=tempo+1
f[u]=tempo
Inizializzazione del grafo e della variabiletempo
for each vertice u V
do if colore[u] = Bianco
then DFS-Visita(G,u)
DFS: simulazone
ba
c
e f
d
DSF(G:grafo)
for each vertice u V
do colore[u] = Bianco
pred[u] = NIL
tempo = 0
for each vertice u V
do if colore[u] = Bianco
then DFS-Visita(G,u)
DSF-Visita(G:grafo,u:vertice)
colore[u] = Grigio
d[u] = tempo = tempo + 1
for each vertice v Adiac[u]
do if colore[v] = Bianco
then pred[v] = u
DFS-Visit(G,v)
colore[u] = Nero
f[u] = tempo = tempo + 1
Alberi di copertura multipli
ab
c de
f
g
DSF(G:grafo)
for each vertice u V
do colore[u] = Bianco
pred[u] = NIL
tempo = 0
for each vertice u V
do if colore[u] = Bianco
then DFS-Visita(G,u)
DSF-Visita(G:grafo,u:vertice)
colore[u] = Grigio
d[u] = tempo = tempo + 1
for each vertice v Adiac[u]
do if colore[v] = Bianco
then pred[v] = u
DFS-Visit(G,v)
colore[u] = Nero
f[u] = tempo = tempo + 1
a
cd
ef
b
g
DSF-Visita(G:grafo,u:vertice)
colore[u] = Grigio
d[u] = tempo = tempo + 1
for each vertice v Adiac[u]
do if colore[v] = Bianco
then pred[v] = u
DFS-Visit(G,v)
colore[u] = Nero
f[u] = tempo = tempo + 1
Tempo di esecuzione di DFSDSF(G:grafo)
for each vertice u V
do colore[u] = Bianco
pred[u] = NIL
tempo = 0
for each vertice u V
do if colore[u] = Bianco
then DFS-Visita(G,u)
(|V|)
(|V|)
Chiamata solo per vertici non ancora visitati
|V|volte
( |Eu| )
Tempo di esecuzione di DFSDSF(G:grafo)
for each vertice u V
do colore[u] = Bianco
pred[u] = NIL
tempo = 0
for each vertice u V
do if colore[u] = Bianco
then DFS-Visita(G,u)
(|V|+ |E| )
BNF - Backus-Naur Form
Backus-Naur Form (BNF) : notazione standard utilizzata per ladefinizioni di grammatiche di linguaggi formali.
Specifica le regole di produzione delle frasi del linguaggio.
Distingue tre insiemi di simboli:• simboli non terminali (<nome>, <cognome>, …)• simboli terminali (a, marco, …) [oppure ‘a’, ‘marco’, …]• simboli speciali ( |, ::= )
Esempio:
<number> ::= <digit> | <digit> <number>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
BNF - Esempi
Alcuni esempi:
<number> ::= <digit> | <digit> <number>
<digit> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
<real_number> ::= <number> '.' <number>
<log_expr> ::= <log_expr> <bin_op> <log_expr> |'' <log_expr> | <simple_exp>
<bin_op> ::= '' | ''
EBNF - Extended Backus-Naur Form
Extended Backus-Naur Form (EBNF) : estende la notazioneBackus-Naur Form (BNF) con simboli aggiuntivi per la definizionidi parti opzionali o ricorrenti:
• parentesi tonde [raggruppamento]• parentesi quadre [componenti opzionali]• parentesi graffe [componenti ripetute]
La sintassi dei linguaggi di programmazione (C, C++, Pascal, …)viene tipicamente essere espressa tramite grammatiche EBNF.
Facilita la definizione di algoritmi di “parsing” per il linguaggi.
EBNF - parentesi tonde
Parentesi tonde ( ) : indicano una singola occorrenza, tipicamentesi usano per alternative annidate.
Esempio:
<N>::= (a | b) c produce stringhe della forma ac o bc
Equivale alla grammatica
<N> ::= <M> c<M> ::= a | b
EBNF - parentesi quadre
Parentesi quadre [ ] : indicano una componente opzionale, 0 o 1occorrenze.
Esempio:
<N> ::= [a] c produce stringhe della forma c oppure ac
– equivale alla grammatica: <N> ::= <M> c<M> ::= ε | a [ε indica la stringa vuota]
Possono contenere alternative annidate
<N> ::= [a | b] c produce stringhe della forma c o ac o bc
– equivale alla grammatica: <N> ::= <M> c<M>::= ε | a | b
EBNF - parentesi graffe
Parentesi graffe { } : indicano una ripetizione di 0 o 1 o più occorrenze.
Esempio:
<N> ::= {a} c produce stringhe della forma c, ac, aac, …, aaaaac, …
– equivale alla grammatica: <N> ::= <M> c<M> ::= ε | a <M>
Possono contenere alternative annidate
<N> ::= {a | b} c produce stringhe della forma c, ac, bc, abc, bac, …
– equivale alla grammatica: <N> ::= <M> c
<M>::= ε | a <M> | b <M>
Parser Ricorsivo
Analizzatore sintattico di tipo top-down, tipicamente di tipo LL(1).
Relativamente semplice da realizzare, data una grammatica in formaEBNF opportuna (i.e., senza ambiguità).
Segue la struttura ricorsiva della grammatica:
• una funzione per ogni simbolo non-terminale• ona o più funzioni per il riconoscimento dei simboli terminali
La funzione per un non-terminale <X> richiama le funzioni per isimboli terminali e non-terminali, seguendo la regola grammaticaledel non-terminal <X> stesso.
Parser Ricorsivo: Esempio
Data la regola EBNF <X> ::= '(' <T> ',' <T> ') ‘ lo schema dellafunzione per <X> è
void parse_X(…)
{ // fn. per il non-terminale <X>
match(‘(’); // lettura del terminale ‘)’
parse_T(…); // chiamata alla fn. per <T>
match(‘,’);
parse_T(…);
match(‘)’);
}
Libreria per gestione grafi
Definire in linguaggio C una libreria per la gestione di grafi. • Implementazione di grafi con le due rappresentazioni
standard: • Rappresentazione tramite matrice di adiacenza; • Rappresentazione tramite liste di adiacenza.
• Prevedere la possibilità di associare pesi (numeri reali) agli archi.
Variabile di tipo puntatorea (descrittore di) file.
Funzioni standard gestione file#include <stdio.h>
FILE *stream fopen(const char *name, char *mode); int fclose(FILE *stream);
#include <stdio.h>
int main(void) {FILE *fd = fopen("myfile.txt","r");char y[5] = "abcd";
if (fd) {fprintf(fd,"%s\n", y); /* inserisce nel file la stringa contenuta in y */fclose(fd);
}}
Funzioni di apertura e chiusura di file.
Funzioni standard gestione file#include <stdio.h>
FILE *stream fopen(const char *name, char *mode); int fclose(FILE *stream);
#include <stdio.h>
int main(void) {FILE *fd = fopen("myfile.txt","r");char y[5] = "abcd";
if (fd) {fprintf(fd,"%s\n", y); /* inserisce nel file la stringa contenuta in y */fclose(fd);
}}
Modalità di apertura file
I possibili valori per il mode in fopen("myfile.txt", mode) sono:
r Apre il file per la lettura (read-only).
w Apre il file per la scrittura (write-only). Il file viene creato se non esiste.
r+ Apre il file per lettura e scrittura. Il file deve già esistere.
w+ Apre il file per scrittura e lettura. Il file viene creato se non esiste.
a Apre il file per aggiornamento (append). È come aprire il file per la scrittura, ma ci siposiziona alla fine del file per aggiungere dati alla fine. Il file viene creato se non esiste.
a+ O Apre il file per lettura e aggiornamento. Il file viene creato se non esiste.
Funzioni standard di output su file#include <stdio.h>
int fputc(char c, FILE *stream);int fprintf(FILE *stream, const char *format, ...);
#include <stdio.h>
int main(void) {FILE *fd = fopen("myfile.txt","r");char y[5] = "abcd";
if (fd) {fprintf(fd,"%s\n", y); /* inserisce nel file la stringa contenuta in y */fclose(fd);
}}
Funzioni di scritture su file.
Funzioni standard di output su file#include <stdio.h>
int fputc(char c, FILE *stream);int fprintf(FILE *stream, const char *format, ...);
#include <stdio.h>
int main(void) {FILE *fd = fopen("myfile.txt","r");char y[5] = "abcd";
if (fd) {for (int i=0; i<4; i++)
fputc(y[i], fd); /* inserisce nel file la stringa contenuta in y */fclose(fd);
}}
Funzioni di scritture su file.
Funzioni standard di input da file
#include <stdio.h>
int fgetc(FILE *stream);
#include <stdio.h>
int main(void) {char x[10];
FILE *fd = fopen("myfile.txt","r");if (fd) {for(i = 0; i < 10; i++)
x[i] = fgetc(); /* Legge 10 caratteri che vengono inseriti nell’array x */ fclose(fd);}
}
Funzioni di scritture su file.
Funzioni standard di spostamento in file#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
long ftell(FILE *stream);
void rewind(FILE *stream);
int fgetpos(FILE *stream, fpos_t *pos);
int fsetpos(FILE *stream, fpos_t *pos);
• fseek: sposta l’indicatore di posizione nel file alla posizione whence + offset
whence può valere SEEK_SET, SEEK_CUR o SEEK_END per specificare il riferimento all'inizio, alla posizione corrente o alla fine file.
• ftell: ritorna il valore corrente dell'indicatore di posizione del file stream.
• rewind: imposta l'indicatore di posizione del file stream all'inizio file.
• fgetpos: scrive in *pos il valore corrente della posizione del file.
• fsetpos: riposiziona il file associato a stream nella posizione indicata in *pos.
Libreria per gestione grafiLe operazioni da implementare sono: • Lettura/scrittura di un grafo da file; • Conversione tre le due le rappresentazioni di grafo (liste di
adiacenza ↔ matrice di adiacenza) • Inserimento di un nuovo vertice; • Inserimento di un nuovo arco; • Cancellazione di un arco o di un vertice; • Calcolo del grafo trasposto; • Visita in ampiezza di un grafo; • Visita in profondità di un grafo; • Stampa di un percorso tra due vertici (minimo o non); • Verifica dell’aciclicità di un grafo.
BNF per file di specifica grafiIl file che contiene la descrizione di un grafo deve essere conforme alla seguentegrammatica in formato Extended-BNF (EBNF):
I simboli tra virgolette identificano i simboli terminali della grammatica. Ad es. “(”indica il simbolo terminale di aperta parentesi (nel file le virgolette non compaiono).• I simboli tra parentesi angolari (ad es., <nodo>) identificano i simboli non
terminali della grammatica.• Le parentesi graffe indicano zero o più ripetizioni del simbolo tra esse contenuto.
<grafo> ::= '(' <numero_nodi> ')' <adiacenze> '.'
<adiacenze> ::= <adiacenza_nodo> { <adiacenze> }
<adiacenza_nodo> ::= <nodo> ['->' <lista_archi> ] ';'
<lista_archi> ::= <arco> { ',' <arco> }
<arco> ::= ['(' <peso> ')' ] <nodo>
<nodo> ::= identificativo
<peso> ::= numero_reale_con_segno
<numero_nodi> ::= numero_intero_senza_segno