Una componente connessa (o semplicemente “componente”) di un grafo indiretto è un sottografo composto da un insieme massimale di nodi connessi da cammini.
grafo connesso
Un grafo indiretto si dice connesso se ha una sola componente.
esempio di grafo con 5 componenti:
Si può calcolare il vettore delle componenti connesse di un grafo . Il vettore () ha tanti elementi quanti sono i nodi del grafo, e e sono nella stessa componente connessa.
Per l’esempio sopra, il vettore sarebbe:
algoritmo per il vettore delle componenti connesse:
def Componenti(x, G, C, c):
C[x] = c
for y in G[x]:
if C[y] == 0:
Componenti(y, G, C, c)
def main(G):
C = [0]*len(G)
c = 0
for x in range(len(G)):
if C[x] == 0:
c+=1
Componenti(x, G, C, c)
return C
- questo algoritmo ha complessità
componente fortemente connessa
Una componente fortemente connessa di un grafo diretto è un sottografo composto da un insieme massimale di nodi connessi da cammini.
grafo fortemente connesso
Come per i grafi indiretti, un grafo diretto si dice fortemente connesso se ha una sola componente.
- un grafo fortemente connesso con più di un nodo è sempre ciclico
grafo fortemente connesso
- ogni nodo deve quindi essere raggiungibile da tutti gli altri nodi
esempio di grafo con 5 componenti:
attenzione
l’algoritmo per le componenti connesse non funziona nel caso di componenti fortemente connesse: infatti, se c’è un cammino da a , non è detto che ce ne sia anche uno da a .
Per scrivere un algoritmo che, dato un grafo diretto ed un suo nodo , calcola i nodi della componente fortemente connessa che contiene , è utile creare il grafo trasposto di .
grafo trasposto
Dato un grafo diretto , il suo grafo trasposto ha gli stessi nodi di , ma archi con direzione opposta.
(quindi, se facciamo una DFS a partire da un nodo , otterremo tutti i nodi che, in , portano a )
algoritmo
Un possibile algoritmo per trovare il vettore delle componenti fortemente connesse funziona quindi così:
- si calcola l’insieme dei nodi raggiungibili da
- semplice visita DFS
- si costruisce il grafo trasposto
- si calcola l’insieme dei nodi che portano a
- visita DFS su
- si restituisce l’intersezione dei due insiemi e -
- a questo punto si hanno due vettori dei visitati (con la stessa cardinalità) - si scorrono semplicemente gli indici e si controlla per quali nodi entrambi hanno valore
algoritmo per trovare la componente fortemente connessa di un nodo:
def ComponenteFC(x, G):
visitati = DFS(x,G)
GT = Trasposto(G)
visitatiT = DFS(x, GT)
componente = []
for i in range(len(G)):
if visitati[i] == visitatiT[i] == 1:
componente.append(i)
return componente
def Trasposto(G):
GT = [ [] for _ in G]
for i in range(len(G)):
for v in G[i]:
GT[v].append(i)
# (metto il nodo sorgente nella lista di adiacenza del n. destinazione)
return GT
questo algoritmo può essere usato come subroutine per trovare le componenti fortemente connesse di tutti i nodi:
algoritmo per trovare il vettore delle componenti FC:
def compFC(G):
FC = [0]*len(G)
c = 0
for i in range(len(G)):
if FC[i] == 0:
E = ComponenteFC(i, G)
# ^ ritorna un array con i numeri dei nodi nella comp.
c+=1
for x in E:
FC[x] = c
return FC
Al caso pessimo, la complessità sarà . Consideriamo il caso di un grafo diretto avente un arco per ogni .
- facciamo visite, di cui ognuna costa
- ma gli archi sono , quindi:
Esistono algoritmi che lavorano in tempo , come l’algoritmo di Tarjan e quello di Kosaraju (da aggiungere !! ma non in programma).