1
Fork 0
mirror of https://github.com/Steffo99/appunti-magistrali.git synced 2024-11-22 10:44:17 +00:00
appunti-steffo/2 - Algoritmi e strutture dati/1 - Appunti/22 - Visitare un grafo.md

3.6 KiB

Visitare un grafo

Come per gli alberi radicati, esistono due modi per visitare un grafo: depth-first search e breadth-first search.

In entrambi i casi, non visito mai due volte lo stesso nodo, e come risultato ottengo molteplici alberi, il cui insieme viene detto foresta di copertura.

Se il grafo che vogliamo visitare è diretto, allora dobbiamo considerare come vicini solo gli archi uscenti

La DFS ci può risultare utile per identificare le componenti connesse di un grafo e identificare eventuali cicli.

Funzionamento

Posso utilizzare la DFS per classificare gli archi di un grafo in quattro categorie:

  • Tree, archi che ci fanno scoprire un nuovo nodo
  • Forward, archi che ci portano a un discendente
  • Back, archi che ci portano ad un antenato
  • Cross, archi che connettono due sottoalberi diversi

Usiamo due array inizializzati a 0 chiamati pre e post, grandi quanto il numero di archi del grafo, che ci indicano rispettivamente quando un nodo è stato scoperto e quando è terminata la visita.
Inoltre, creiamo una variabile clock che avanza ad ogni evento.
Alla scoperta di un nuovo nodo, mettiamo il valore attuale di clock all'interno di pre[n].
Alla fine della visita di un nodo invece mettiamo il valore di clock in post[n].

Durante la visita, gli archi avranno i seguenti valori:

  • Tree: pre[dst] == 0
  • Forward: pre[src] < pre[dst] && post[dst] > 0
  • Back: pre[dst] < pre[src] && post[dst] == 0
  • Cross: Tutti gli altri (post[dst] < pre[src])

A fine visita, gli archi avranno i seguenti valori:

  • Tree: pre[dst] < pre[dst] < post[dst] < pre[src]
  • Forward: pre[dst] < pre[dst] < post[dst] < pre[src]
  • Back: pre[src] < pre[dst] < post[dst] < post[src]
  • Cross: pre[dst] < post[dst] < pre[src] < post[src]

Se un grafo non diretto contiene degli archi Back, allora esso conterrà un ciclo.

DFS nel grafo trasposto

Se effettuo una DFS sul trasposto di un grafo, posso scoprire i nodi che hanno un cammino verso l'origine.

DFS nella componente fortemente connessa

Se effettuo una DFS in una componente fortemente connessa e nella sua trasposta, il post della trasposta sarà sempre minore del post della componente originale.

Costo computazionale

Categoria Upper bound Lower bound Tight bound
Tempo O(nodi + archi) Ω(nodi + archi) θ(nodi + archi)
Memoria O(nodi) Ω(nodi) θ(nodi)

Visualizzazione

visualgo.net

La BFS ci può risultare utile per trovare tutti i nodi a una certa distanza da un'origine.

Costo computazionale

Categoria Upper bound Lower bound Tight bound
Tempo O(nodi + archi) Ω(nodi + archi) θ(nodi + archi)
Memoria O(nodi + archi) Ω(nodi + archi) θ(nodi + archi)

Pseudocodice

Come per gli alberi, la implementiamo in modo iterativo:

queue = [starting_node]
parents = [None for node in graph.nodes]
distance = [-1 for node in graph.nodes]

# TODO: controllami quando sei più sveglio

while queue:
    node, source, distance = queue.pop(0)
    parents[node.number] = source
    distance[node.number] = distance
    for neighbour in node.neighbours:
        queue.append((neighbour, node, distance+1))

Nella coda, la distanza massima tra un nodo e l'altro è 1.

Visualizzazione

visualgo.net