distanza minima

Dati due nodi e di un grafo , definiamo distanza minima in di da il numero minimo di archi che bisogna attraversare per raggiungere a partire da .

  • la distanza è posta a se non è raggiungibile partendo da

visita in ampiezza

La visita in ampiezza esplora i nodi partendo da quelli a distanza dalla sorgente . Poi visita quelli a distanza e così via.

  • l’algoritmo visita quindi tutti i nodi a livello prima di passare a quelli a livello .

center

L’algoritmo genera un albero detto albero BFS.

Per effettuare questa visita, si mettono in una coda i nodi visitati i cui adiacenti non sono ancora stati esaminati. Ad ogni passo, si prende il primo nodo dalla coda, si esaminano i suoi adiacenti e, se si scopre un nuovo nodo, si visita e si aggiunge alla coda.

center

^ sulla destra i tre alberi BFS risultanti da tre visite BFS di a partire da , , rispettivamente

implementazione naïf

processo:

  • si comincia con una coda contenente solo il nodo di partenza .
  • finché la coda non risulta vuota, ad ogni passo:
    • un nodo viene estratto dalla coda
    • tutti i suoi adiacenti vengono visitati e messi in coda
def BFS(x, G):
	visitati = [0]*len(G)
	visitati[x] = 1
	coda = [x]
 
	while coda:
		u = coda.pop()
		for y in G[u]:
			if visitati[y] == 0:
				visitati[y] = 1
				coda.append(y) # va in coda solo se non visitato
	return visitati
  • un nodo finisce in coda al più una volta, quindi il while viene eseguito volte
  • le liste di adiacenza dei nodi verranno scorse al più una volta, quindi il costo totale dei for y in G[u] è
  • però, l’estrazione in testa da una lista (pop()) ha costo proporzionale al numero di elementi presenti nella lista, ovvero anche

quindi, il costo totale dell’algoritmo è .

implementazione in

Se si potesse eseguire un pop() in , l’algoritmo costerebbe - basta non eseguire effettivamente le cancellazioni, ma semplicemente utilizzare un puntatore per indicare l’inizio della coda all’interno della lista (incrementandolo ogni volta che si “cancella” dalla coda).

def BFS(x, G):
	visitati = [0]*len(G)
	visitati[x] = 1
	coda = [x]
	i = 0
	while len(coda) > i:
		u = coda[i]
		i += 1
		for y in G[u]:
			if visitati[y] == 0:
				visitati[y] = 1
				coda.append(y)
	return visitati

albero BFS

La procedura può essere modificata in modo da restituire in l’albero di visita BFS rappresentato tramite vettore dei padri.

albero BFS

center

def BFSpadri(x, G):
	P = [-1]*len(G)
	P[x] = [x]
	coda = [x]
	i = 0
	while len(coda) > i:
		u = coda[i]
		i += 1
		for y in G[u]:
			if P[y] == -1:
				P[y] = u
				coda.append(y)

cammino

grazie al vettore dei padri , come già visto per la visita DFS, con la procedura , si può ottenere in un cammino dalla radice dell’albero al nodo .

Però, in più, vale anche:

la distanza minima di un vertice da (radice) nel grafo equivale alla profondità di nell'albero BFS.

dimostrazione di da .

  • caso base: è banalmente vero per , in quanto è l’unico vertice a distanza da se stesso, ed ha profondità nell’albero.
  • ipotesi induttiva: supponiamo sia vero per tutti i vertici a distanza al più .

Consideriamo un vertice a distanza . Sia un cammino minimo da a , e sia il predecessore di in questo cammino. Per ipotesi induttiva sappiamo che è a profondità .

  • se è stato inserito nell’albero grazie a (è stato esplorato partendo da ), allora sappiamo che si troverà a distanza .

Supponiamo quindi che sia stato inserito grazie ad un nodo .

La profondità di non può essere inferiore a , o avremmo trovato un cammino di lunghezza inferiore a (il che contraddirebbe l’ipotesi iniziale). Ma la profondità di non può essere maggiore di , perché il nodo sarebbe stato visitato prima di e sarebbe stato inserito grazie a . Quindi, si ha necessariamente che la profondità di è , e che la profondità di è .

vettore delle distanze

La procedura può anche essere modificata in modo da restituire in il vettore delle distanze .

  • al nodo viene assegnata distanza zero, e a tutti gli altri - a ciascun nodo via via visitato viene assegnata la distanza del padre incrementata di .
  • al termine della procedura, conterrà se il nodo non è raggiungibile da , e la distanza minima di da altrimenti.
def BFSdistanze(x, G):
	D = [-1]*len(G)
	D[x] = 0
	coda = [x]
	i = 0
	while len(coda) > i:
		u = coda[i]
		i += 1
		for y in G[u]:
			if D[y] == -1:
				D[y] = D[u] + 1
				coda.append(y)
	return D