Rilevare cicli in grafi orientati è un problema fondamentale; ad esempio, nel contesto delle dipendenze, un ciclo indica una dipendenza circolare, che è un’incoerenza logica.

Si potrebbe pensare che la strategia più semplice da implementare sia una DFS che si ferma appena si imbatte in un arco che punta a un nodo già visitato, ma questa strategia è errata, perché non distingue tra i tipi diversi di archi.

Ad esempio la DFS che parte da nel grafo sarebbe un falso positivo.

La vera condizione per trovare un ciclo in un grafo ORDINATO è l’incontro di un arco all’indietro, cioè un arco , dove è un antenato (non padre) di .

Nel concreto, è un antenato di se, mentre viene visitato, è ancora in visita.

Implementazione con liste di adiacenza

L’algoritmo usa la lista visitati, a valori:

  • 0: il nodo corrispondente all’indice non è stato visitato,
  • 1: il nodo corrispondente all’indice è in visita,
  • 2: il nodo è stato visitato.
def ha_cicli(liste):
	n = len(liste)
	visitati = [0] * n
	for nodo in range(n):
		if visitati[nodo] == 0:
			if dfs(nodo, liste, visitati):
				return True
	return False
 
def dfs(nodo, liste, visitati):
	visitati[nodo] = 1
	for vicino in liste[nodo]:
		if visitati[nodo] == 0:
			if dfs(vicino, liste, visitati):
				return True
		elif visitati[vicino] == 1:
		# il nodo e un suo vicino sono entrambi in visita contemporaneamente -> c'è un arco all'indietro -> c'è un ciclo
			return True
	visitati[nodo] = 2
	return False