La struttura dati più naturale per rappresentare un insieme di oggetti legati da una generica relazione tra loro è il grafo. Formalmente un grafo è una coppia
Due nodi collegati da un arco si dicono adiacenti. Un cammino tra due nodi
È possibile implementare un grafo con una lista di adiacenza o matrice di adiacenza.
Una lista di adiacenza è un vettore di liste lungo
Una matrice di adiacenza è una matrice di valori booleani
Un grafo è detto orientato se la coppia di noti che costituisce un arco è ordinata, o in altre parole se ciò che collega i nodi ha un verso. Un grafo orientato viene rappresentato indicando gli archi come frecce che puntano al secondo nodo della coppia. In un grafo non orientato, l'insieme di archi può essere rappresentato in modo compatto dato che se
Un grafo è detto connesso se esiste un percorso per ogni coppia di nodi.
Un grafo è detto completo se esiste un arco per ogni coppia di nodi.
Un percorso è un ciclo se il nodo di inizio e di fine coincidono, un grafo privo di cicli è detto aciclico.
Nei grafi non orientati, siccome la matrice di adiacenza è simmetrica rispetto alla diagonale posso stoccarne solo metà, mentre nelle liste di adiacenza posso stoccare solo uno dei due archi raddoppiando il tempo di ricerca per un nodo adiacente.
Come per le visite degli alberi, è possibile stampare i nodi in una visita di un grafo, VisitaAmpiezza
si può quindi trasformare in algoritmo di ricerca. Ha complessità totale
VisitaAmpiezza(G,s)
for each n in V except s
n.color <- white
n.dist <- infinity
s.color <- grey
s.dist <- 0
Q <- 0
Enqueue(Q,s)
while !isEmpty(Q)
curr <- Dequeue(Q)
for each v in curr.adiacenti
if v.color = while
v.color <- grey
v.dist <- curr.dist + 1
Enqueue(Q,v)
curr.color <- black
È anche possibile visitare in profondità, dove, diversamente dalla visita in ampiezza visitiamo prima i nodi adiacenti a quello dato, e poi il nodo stesso.
È detta componente connessa di un grafo
ComponentiConnesse(G)
for each v in V
v.etichetta <- -1
eti <- 1
for each v in V
if v.etichetta = -1
VisitaEdEtichetta(G,v,eti)
eti <- eti + 1
Dove il metodo VisitaEdEtichetta
funziona come VisitaAmpiezza
o VisitaProfondità
ma imposta a eti
il campo etichetta
del nodo visitato.
Dato un grafo orientato, definiamo il predecessore di un nodo
Un valore utile da calcolare per un grafo orientato aciclico è il cosiddetto ordinamento topologico. L'ordinamento topologico è una sequenza di nodi del grafo tale per cui nessun nodo compare prima di un suo predecessore. L'ordinamento topologico non è unico.
Se il grafo non è connesso le componenti connesse possono essere ordinate in qualunque modo l'una rispetto all'altra.
VisitaProfOT(G,s,L)
s.color <- grey
for each v in curr.adiacenti
if v.color = white
VisitaProfOT(G,v,L)
s.color <- black
PushFront(L,s)
OrdinamentoTopologico(G)
for each v in V
v.color <- white
for each v in V
if v.color = white
VisitaProfOT(G,v,L)
return L
L'algoritmo di Dijkstra trova, dato un grafo orientato e un suo nodo
Funziona inserendo ogni v.pred
a NIL
. Inserisco s.dist <- 0
e s.pred <- NIL
. Quindi, fino a quando c
con dist
minima e controllo per ogni adiacente a
se hanno distanza minore di c.dist + peso(c,a)
. Se questo accade imposto a.pred <- c
e a.dist <- c.dist + peso(c,a)
.
È possibile migliorare la complessità memorizzando l'insieme
DijkstraQueue(G,s)
Q <- 0
s.dist <- 0
for each v in V
if v != s
v.dist <- infinity
v.pred <- NIL
AccodaPri(Q,v,v.dist)
while Q != 0
u <- CancellaMin(Q)
for each v in u.succ
ndis <- u.dist + peso(u,v)
if v.dist > ndis
v.dist <- ndis
u.prev <- u
DecrementaPri(Q,v,ndis)
Dato un grafo orientato, in cui ogni nodo ha un solo successore, l'algoritmo di Floyd determina, a partire da un nodo iniziale se il cammino genera un ciclo. Ha complessità
FloydLT(G,x)
t <- x.succ
l <- x.succ.succ
while l != t
t <- t.succ
l <- l.succ.succ
T <- 0
t <- x
while l != t
t <- t.succ
l <- l.succ
T <- T + 1
l <- t
C <- 0
while l != t
l <- l.succ
C <- C + 1
return T,C