T - Grafi Flashcards
Definizione di ADT Grafo statico, semi-statico, dinamico.
- statico: non cambia
- semi-statico: si possono cancellare solo gli archi
- dinamico: si può cancellare tutto
Rappresentazioni possibili degli archi: descrizione, complessità spaziale, vantaggi e svantaggi.
1) Matrice di adiacenza: è una matrice adj |V| x |V| in cui l’elemento (i, j) vale 1 (o il peso nel caso di un grafo pesato) se l’arco (i, j) appartiene ad E.
È una matrice simmetrica se il grafo è non orientato.
È svantaggioso perché la complessità spaziale è S(n) = Theta(|V|^2).
È vantaggiosa perché non ci sono costi aggiuntivi per i grafi pesati e perché la lettura è diretta, O(1).
2) Lista di adiacenza: è un vettore A di |V| elementi in cui ogni elemento A[i] contiene il puntatore alla lista dei vertici adiacenti a i.
Gli elementi complessivi nelle liste sono al massimo 2|E| per i grafi non orientati e |E| per quelli orientati, per cui S(n) = O(max(|V|, |E|)) = O(|V + E|), e per questo è vantaggioso.
È svantaggioso perché c’è bisogno di un campo aggiuntivo per i pesi e perché la ricerca comporta la scansione della lista.
Come sono organizzati i file per l’elaborazione di un grafo? E le strutture dati? (da aggiornare con le altre slide dei grafi)
Bisogna lavorare con il main (client), con i file per i grafi e con quelli per la conversione indice_vertice <–> nome_vertice
main.c
#include
Graph.h
#include
typedef struct { int v; int w; int wt; } Edge;
typedef struct graph *Graph;
Graph.c
#include
// se si usa la lista delle adiacenze:
typedef struct node* link;
struct node {int v; int wt; link next; };
struct graph { int V; int E; link *ladj; ST tab; link z; };
// mentre se si usa la matrice delle adiacenze:
struct graph { int V; int E; int **mat; ST tab; };
ST.h
typedef struct symbtab *ST;
ST.c
struct symbtab {char **a; int MAXN; int N; }
GRAFI ORIENTATI E NON, LISTA DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- Graph GRAPHinit(int V);
- void GRAPHfree(Graph G);
Graph.h
typedef struct edge {int v; int w; //int wt; } Edge;
typedef struct graph *Graph;
Graph GRAPHinit(int V);
void GRAPHfree(Graph G);
Graph.c
typedef struct node * link;
struct node {int v; //int wt; link next; };
struct graph {int V; int E; link *ladj; ST tab; link z; };
Graph GRAPHinit(int V){
Graph g = malloc(sizeof(*g));
if (g == NULL) return NULL;
g->V = V;
g->E = 0;
g->z = NEW(-1, 0, NULL);
g->ladj = malloc(V *sizeof(link));
if (g->ladj == NULL) return NULL;
for (int i = 0; i < g->V; i++){
g->ladj[i] = z;
}
g->tab = STinit(V);
return g;
}
void GRAPHfree(Graph G){
link t;
for (int i = 0; i < G->V; i++){
for (link x = G->ladj[i]; x != G->z; x = t){
t = x->next;
free(x);
}
}
free(G->ladj);
STfree(tab);
free(z);
free(G);
}
GRAFI ORIENTATI E NON, LISTA DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- Graph GRAPHload(FILE *fin);
Graph.h
typedef struct edge {int v; int w; //int wt; } Edge;
typedef struct graph *Graph;
Graph GRAPHload(FILE *fin);
Graph.c
typedef struct node * link;
struct node {int v; //int wt; link next; };
struct graph {int V; int E; link *ladj; ST tab; link z; };
Graph GRAPHload(FILE *fin){
Graph G;
int V, i, wt;
char label1[MAX], label2[MAX];
fscanf(fin, “%d”, &V);
G = GRAPHinit(V);
for (i = 0; i < V; i++){
fscanf(fin, “%s”, label1);
STinsert(G->tab, label1, i);
}
while (fscanf(fin, “%s %s %d”, label1, label2, &wt) == 3){
id1 = STsearch(G->tab, label1);
id2 = STsearch(G->tab, label2);
if (id1 >= 0 && id2 >=0) GRAPHinsertE(G, id1, id2, wt);
}
}
void GRAPHinsertE(Graph G, int id1, int id2, int wt) {
insertE(G, EDGEcreate(id1, id2, wt));
}
static Edge EDGEcreate(int v, int w, int wt) {
Edge e;
e.v = v; e.w = w; e.wt = wt;
return e;
}
// NON ORIENTATI
static void insertE(Graph G, Edge e) {
int v = e.v, w = e.w, wt = e.wt;
G->ladj[v] = NEW(w, wt, G->ladj[v]);
G->ladj[w] = NEW(v, wt, G->ladj[w]);
G->E++;
}
//ORIENTATI
static void insertE(Graph G, Edge e) {
int v = e.v, w = e.w, wt = e.wt;
G->ladj[v] = NEW(w, wt, G->ladj[v]);
G->E++;
}
GRAFI ORIENTATI E NON, LISTA DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- void GRAPHstore(Graph G, FILE *fout);
- void GRAPHedges(Graph G, Edge *a);
Graph.h
typedef struct edge {int v; int w; //int wt; } Edge;
typedef struct graph *Graph;
Graph GRAPHload(FILE *fin);
Graph.c
typedef struct node * link;
struct node {int v; //int wt; link next; };
struct graph {int V; int E; link *ladj; ST tab; link z; };
void GRAPHstore(Graph G, FILE *fout) {
int i;
Edge *a;
a = malloc(G->E * sizeof(Edge));
if (a == NULL) return;
GRAPHedges(G, a);
fprintf(fout, “Numero vertici: %d\n”, G->V);
for (i = 0; i < G->V; i++)
fprintf(fout, “%s\n”, STsearchByIndex(G->tab, i));
for (i = 0; i < G->E; i++)
fprintf(fout, “%s %s %d\n”, STsearchByIndex(G->tab, a[i].v),
STsearchByIndex(G->tab, a[i].w), a[i].wt);
}
// NON ORIENTATI
void GRAPHedges(Graph G, Edge *a) {
int v, E = 0;
link t;
for (v=0; v < G->V; v++)
for (t=G->ladj[v]; t != G->z; t = t->next)
if (v < t->v)
a[E++] = EDGEcreate(v, t->v, t->wt);
}
// ORIENTATI
void GRAPHedges(Graph G, Edge *a) {
int v, E = 0;
link t;
for (v=0; v < G->V; v++)
for (t=G->ladj[v]; t != G->z; t = t->next)
a[E++] = EDGEcreate(v, t->v, t->wt);
}
GRAFI ORIENTATI E NON
Codice delle funzioni:
- int GRAPHgetIndex(Graph *G, char * label);
- void GRAPHremoveE(Graph G, int id1, int id2); (sia lista che matrice)
int GRAPHgetIndex(Graph G, char * label){
int id;
id = STsearch(G->tab, label);
if (id == -1) {
id = STsize(G->tab);
STinsert(G->tab, label, id);
}
return id;
}
void GRAPHremoveE(Graph G, int id1, int id2) {
removeE(G, EDGEcreate(id1, id2));
}
static void removeE(Graph G, Edge e) {
int v = e.v, w = e.w;
link x;
if (G->ladj[v]->v == w) {
G->ladj[v] = G->ladj[v]->next;
G->E–;
}
else
for (x = G->ladj[v]; x != G->z; x = x->next)
if (x->next->v == w) {
x->next = x->next->next;
G->E–;
}
// nei grafi orientati non c’è questa seconda parte
if (G->ladj[w]->v == v)
G->ladj[w] = G->ladj[w]->next;
else
for (x = G->ladj[w]; x != G->z; x = x->next)
if (x->next->v == v)
x->next = x->next->next;
}
static void removeE(Graph G, Edge e) {
int v = e.v, w = e.w, int wt = e.wt;
if (G->madj[v][w] != maxwt)
G->E–;
G->madj[v][w] = maxwt;
G->madj[w][v] = maxwt; // solo grafi non orientati
}
}
GRAFI ORIENTATI E NON, MATRICE DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- Graph GRAPHinit(int V);
- void GRAPHfree(Graph G);
Graph.h
#include
typedef struct graph * Graph;
typedef struct {int v; int w; int wt; } Edge;
Graph.c
#include
#define maxwt INT_MAX
struct graph { int V; int E; int **madj; ST tab; };
static int **MATRIXint(int r, int c, int val);
static int MATRIXint(int r, int c, int val){
int i, j;
int **m = malloc(rsizeof(int));
if (m == NULL) return NULL;
for (i = 0; i < r; i++){
m[i] = malloc(c*sizeof(int);
if (m[I] == NULL) return NULL;
}
for (i = 0; i < r; i++)
for(j = 0; j < c; j++)
m[i][j] = val;
return m;
}
Graph GRAPHinit(int V){
Graph G = malloc(sizeof(*G));
if (G == NULL) return NULL;
G->V = V;
G->E = 0;
G->madj = MATRIXint(V, V, maxwt);
if (G->madj == NULL) return NULL;
G->tab = STinit(V);
if (G->tab == NULL) return NULL;
return G;
}
void GRAPHfree(Graph G){
int i, j;
for (i = 0; i < G->V; i++) free(G->madj[i]);
free(G->madj);
STfree(G->tab);
free(G);
}
GRAFI ORIENTATI E NON, MATRICE DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- Graph GRAPHload(FILE *fin);
Graph GRAPHload(FILE *fin){
int V, id1, id2, wt;
char label1[MAX], label2[MAX];
fscanf(“%d”, &V);
Graph G = GRAPHinit(V);
for (int i = 0; i < V; i++){
fscanf(“%s”, label1);
STinsert(G->tab, label1, i);
}
while(fscanf(“%s %s %d”, label1, label2, &wt) == 3){
id1 = STsearch(G->tab, label1);
id2 = STsearch(G->tab, label2);
if(id1≥0 && id2≥0) GRAPHinsertE(G, id1, id2, wt);
}
return G;
}
void GRAPHinsertE(Graph G, int id1, int id2, int wt) {
insertE(G, EDGEcreate(id1, id2, wt));
}
static Edge EDGEcreate(int v, int w, int wt){
Edge e;
e.v = v; e.w = w; e.wt = wt;
return e;
}
static void insertE(Graph G, Edge e) {
int v = e.v, w = e.w, wt = e.wt;
if (G->madj[v][w] == maxWT) G->E++;
G->madj[v][w] = wt;
G->madj[w][v] = wt; // solo per grafi non orientati
}
GRAFI ORIENTATI E NON, MATRICE DELLE ADIACENZE (pesati e non)
Scrivere il codice delle seguenti funzioni:
- void GRAPHstore(Graph G, FILE *fout);
- void GRAPHedges(Graph G, Edge *a);
void GRAPHstore(Graph G, FILE *fout) {
int i;
Edge *a = malloc(G->E * sizeof(Edge));
if (a == NULL) return;
GRAPHedges(G, a);
fprintf(fout, “Numero vertici: %d\n”, G->V);
for (i = 0; i < G->V; i++)
fprintf(fout, “%s\n”, STsearchByIndex(G->tab, i));
for (i = 0; i < G->E; i++)
fprintf(fout, “%s %s %d\n”, STsearchByIndex(G->tab, a[i].v),
STsearchByIndex(G->tab, a[i].w), a[i].wt);
}
void GRAPHedges(Graph G, Edge *a) {
int v, w, E = 0;
for (v=0; v < G->V; v++)
for (w=v+1; w < G->V; w++)
if (G->madj[v][w] != 0)
a[E++] = EDGEcreate(v, w, G->madj[v][w]);
}
// ORIENTATI
void GRAPHedges(Graph G, Edge *a) {
int v, w, E = 0;
for (v=0; v < G->V; v++)
for (w=0; w < G->V; w++)
if (G->madj[v][w] != 0)
a[E++] = EDGEcreate(v, w, G->madj[v][w]);
}
Quali strategie abbiamo studiato per la visita di un grafo?
- Depth First Search
- Breadth First Search
A cosa quali tipi di grafo è applicabile la visita DFS? Cosa genera?
La DFS è applicabile a grafi connessi e non.
Genera un albero della visita in profondità.
DFS: Cosa si intende con profondità?
Con profondità si intende l’espansione dell’ultimo vertice scoperto che ha ancora vertici non ancora scoperti a lui adiacenti.
DFS: Come vengono trattati i vertici?
La DFS parte da un vertice e visita tutti i vertici del grafo, sia che siano da essi raggiungibili che non.
Ogni vertice viene etichettato con il tempo di scoperta e con il tempo di fine elaborazione: pre[i] e post[i]
Per ogni vertice viene memorizzato il padre, ovvero il vertice a partire dal quale può essere raggiunto.
I vertici vengono rappresentati concettualmente come bianchi, grigi e neri.
DFS: come vengono etichettati gli archi?
Ogni arco può essere etichettato come T, B, F, C (solo T e B se il grafo è non orientato).
- T, tree: è il tipo di arco che si genera nella ricorsione
- B, backward: è l’arco che connette un vertice j a un suo antenato i per cui vale che post[i] == -1 ovvero non è ancora stato completato
- F, forward: connette un vertice i a un suo discendente j per cui si ha che pre[i] < pre[j]
- C: sono gli archi rimanenti
Scrivere il codice della void GRAPHdfs(Graph G, int id);(solowrapper)
void GRAPHdfs(Graph G, int id) {
int v, time=0, *pre, *post, *st;
pre = malloc(G->V * sizeof(int));
post = malloc(G->V * sizeof(int));
st = malloc(G->V * sizeof(int));
if ((pre == NULL) || (post == NULL) || (st == NULL)) return;
for (v=0; v < G->V; v++) {
pre[v] = -1; post[v] = -1; st[v] = -1;}
dfsR(G, EDGEcreate(id, id), &time, pre, post, st);
for (v=0; v < G->V; v++)
if (pre[v]== -1) dfsR(G, EDGEcreate(v,v), &time, pre, post, st);
printf(“discovery/endprocessing time labels \n”);
for (v=0; v < G->V; v++)
printf(“vertex %s : %d/%d \n”, STsearchByIndex(G->tab, v), pre[v],
post[v]);
printf(“resulting DFS tree \n”);
for (v=0; v < G->V; v++)
printf(“parent of vertex %s is vertex %s \n”, STsearchByIndex(G->tab,
v), STsearchByIndex(G->tab, st[v]));
}
Scrivere il codice della static void dfsR(Graph G, Edge e, int *time, int *pre, int *post, int *st);
static void dfsR(Graph G, Edge e, int *time, int *pre, int *post, int *st) {
link t;
int v, w = e.w;
Edge x;
if (e.v != e.w)
printf(“edge (%s, %s) is tree \n”, STsearchByIndex(G->tab, e.v),
STsearchByIndex(G->tab, e.w)) ;
st[e.w] = e.v;
pre[w] = (time)++;
for (t = G->ladj[w]; t != G->z; t = t->next)
if (pre[t->v] == -1)
dfsR(G, EDGEcreate(w, t->v), time, pre, post, st);
else {
v = t->v;
x = EDGEcreate(w, v);
if (post[v] == -1)
printf(“edge (%s, %s) is back \n”, STsearchByIndex(G->tab, x.v),
STsearchByIndex(G->tab, x.w));
else
if (pre[v]>pre[w])
printf(“edge (%s, %s) is forward \n”, STsearchByIndex(G->tab,
x.v), STsearchByIndex(G->tab, x.w));
else
printf(“edge (%s, %s) is cross \n”, STsearchByIndex(G->tab, x.v),
STsearchByIndex(G->tab, x.w));
}
post[w] = (time)++;
}
DFS: lista delle adiacenze
Complessità di inizializzazione, visita ricorsiva, tempo (lista e matrice)
- inizializzazione: Theta(V)
- visita ricorsiva: Theta(E)
- lista: T(n) = Theta(V+E)
- matrice: T(n): Theta(V^2)
BFS: a che tipo di visita porta
Dato un vertice di partenza, si visitano solo quelli da esso raggiungibili
BFS: cosa calcola di fatto?
Calcola la distanza minima dal vertice di partenza
BFS: cosa genera?
Un albero di visita in ampiezza
BFS: trattamento vertici
Ogni vertice viene etichettato con il tempo di scoperta pre[i] e per ogni vertice viene indicato il padre.
BFS: quale struttura dati viene utilizzata? Cosa permette?
Viene utilizzato un ADT di I classe coda e in base a come viene implementata varia la complessità.
La coda è una modalità seriale che permette di emulare un processo di visita dei vertici parallelo.
BFS: come vengono visitati i vertici?
In modo parallelo.