Esempi di grafi
Algoritmi e Strutture Dati
Capitolo 9 - Grafi
Alberto Montresor
Università di Trento
This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. To view a
copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or send a letter to Creative
Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA.
© Alberto Montresor
1
Problemi sui grafi
✦
Grafi orientati e non orientati: definizione
Visite
✦
Visite in ampiezza (numero di Erdös)
✦
Visite in profondità (ordinamento topologico, componenti (fortemente) connesse)
✦
Un grafo orientato G è una coppia (V, E) dove:
✦
✦
✦
Cammini minimi
✦
Da singola sorgente
✦
Fra tutte le coppie di vertici
✦
Alberi di connessione minimi
✦
Problemi di flusso
✦
....
© Alberto Montresor
2
© Alberto Montresor
✦
Insieme finito dei vertici V
✦
Insieme degli archi E: relazione
binaria tra vertici
2
Un grafo non orientato G è una
coppia (V, E) dove:
✦
Insieme finito dei vertici V
Insieme degli archi E: coppie non ordinate
2
3
3
A
1
1
6
4
3
V = {1, 2, 3, 4, 5, 6 }
E = { (1,2), (1,4), (2,3), (4,3),
(5,3), (4,5), (4,1) }
© Alberto Montresor
5
6
4
5
V = {1, 2, 3, 4, 5, 6 }
E = { [1,2], [1,4], [2,3], [3,4],
[3,5], [4,5] }
4
oi ad inventarne una fittizia.
discuteremo
dei
principali
approcci per realizzare la struttura di
Definizioni:
incidenza
e adiacenza
otesi semplificativa che l’insieme statico degli n nodi possa essere
un eseguire
grafo orientato
1 a ✦n.InPer
un’operazione su tutti i nodi e su tutti gli archi,
✦
un segue:
arco (u,v) si dice incidente da u in v
each come
✦
Rappresentazione grafi
✦
In un grafo non orientato
la relazione di incidenza tra vertici è simmetrica
) do
e sull’arco
(u, v)
✦ Un vertice
v si dice adiacente a u ✦
✦
(1,2) è incidente da 1 a 2
(1,4) è incidente da 1 a 4
(4,1) è incidente da 4 a 1
se e solo se (u, v) ∈ E
utti i nodi del grafo,
mentre il secondo scorre tutti i nodi adiacenti
2
azione. Per semplificare il testo3inoltre, useremo
il termine
2 è adiacente
ad 1G.n
1
).size().
3 è adiacente a 2, 4, 5
6
on matrici
4
5
✦
1 è adiacente a 4 e viceversa
2 non è adiacente a 3,4
6 non è adiacente ad alcun vertice
ici per rappresentare un grafo è basata su matrici. Dato un grafo
diacenza M = [muv ] (detta anche matrice di adiacenza nodi-nodi)
Matrice di adiacenza: grafo orientato o non orientato
n ⇥ n, tale che:
5
© Alberto Montresor
muv =
1, se (u, v) ⌅ E,
0, se (u, v) ⌅
/ E.
2
3
n = |V|
numero di nodi
✦
m = |E|
numero di archi
Matrice di adiacenza
✦
Spazio richiesto O(n2)
✦
Verificare se il vertice u è adiacente a v richiede tempo O(1)
✦
Elencare tutti gli archi costa O(n2)
Liste / vettori di adiacenza
✦
Spazio richiesto O(n+m)
✦
Verificare se il vertice u è adiacente a v richiede tempo O(n)
✦
Elencare tutti gli archi costa O(n+m)
6
© Alberto Montresor
G.adj(u) = { v | (u,v) ∈ E }
4
to, la matrice è simmetrica, ovvero muv = mvu
1 per
0 ogni
1 u,0v. Un
1
nza è mostrata nella Fig. 9.6.
2
0 0 1 0
e è banale verificare
in
tempo
O(1)
se
un
dato
arco
è presente nel
2
3 adj0(u) 0di un
0 qual0
il tempo per ricavare l’insieme di
3 adiacenza G.
1
, indipendentemente
dalla cardinalità di G.adj4(u), 1perché
0 occorre
1 0
matrice alla ricerca degli elementi muv = 1. Inoltre, lo spazio
5
0 0 0 1
6
n2 ) e il tempo per esaminare tutti gli archi è (n2 ), anche se il
6
0è pesato,
0 0 allora
0
4 di archi che è O(n).
ene un numero m
Se il grafo
5
o i pesi degli archi al posto degli elementi binari. Se puv è il peso
trice M diventa:
© Alberto Montresor
✦
Liste di adiacenza: grafo orientato
Spazio: n2
1
Poniamo
Spazio: a⋅n + b⋅m
a
5
6
0
0
1
2
0
0
2
3
nil
1
0
3
4
nil
0
0
4
1
0
0
5
3
0
0
2
3
1
6
4
7
© Alberto Montresor
6
b
4
nil
5
nil
nil
nil
5
8
⇧i ⌅ [j + 1, ultimo] : A[i] ⇤ A[j]
Vettore di adiacenza: grafo orientato
G.adj(u) = { v | (u,v) ∈ E }
muv =
a
2
3
1
6
4
2
2
3
3
4
4
1
5
3
6
nil
5
Liste/vettore di adiacenza: grafo non orientato
G.adj(u) = { v | [u,v] ∈ E or [v,u] ∈ E}
a
1
2
3
A
1
3
4
6
4
b
1
© Alberto Montresor
2
foreach u ⌅ G.V() do
Matrice di adiacenza: grafo non orientato
foreach v ⌅ G.adj(u) do
⌅ (u, v)
fai un’operazione sull’arco
1, se [u, v] ⌅ E,
Spazio: a⋅n + b⋅m
5
5
6
4
0, se [u, v] ⌅
/ E.
Il primo foreach scorre tutti i nodi del grafo, mentre il secondo scorre tutti i nodi
1 adiacenti
2
3
al nodo u preso in considerazione. Per semplificare il testo inoltre, useremo il termine G.n
0
1
0
1
come abbreviazione di G.V().size().
2
5
6
1
0
0
1
0
1
0
0
0
3
0
1
0
1
1
0
Uno dei modi più semplici per rappresentare un grafo è basata su matrici.
1 un0 grafo1
4 Dato
5
6 di adiacenza nodi-nodi)
G = (V, E), la matrice di adiacenza M = [muv ] (detta anche matrice
5
0
0
1
è una matrice di dimensione n ⇥ n, tale4 che:
5
6
0
0
0
1, se (u, v) ⌅ E,
muv =
0, se (u, v) ⌅
/ E.
0
1
0
1
0
0
0
0
0
1
Se il grafo non è orientato, la matrice è simmetrica, ovvero muv = mvu per ogni u, v. Un
esempio di matrice
è mostrata nella Fig. 9.6.
9 di adiacenza
© Alberto Montresor
Con questa realizzazione è banale verificare in tempo O(1) se un dato arco è presente nel
grafo oppure no. Purtroppo,
il tempo
Grafi
pesati per ricavare l’insieme di adiacenza G.adj(u) di un qualsiasi nodo u è sempre (n), indipendentemente dalla cardinalità di G.adj(u), perché occorre
scandire un’intera riga della
matricecasi
allaogni
ricerca
degli
elementi
muv =
1. Inoltre,associato
lo spazio
✦ In alcuni
arco
ha un
peso (costo,
guadagno)
2
2
Spazio: a⋅n + di
2⋅b⋅m
memoria occupato è (n✦ ) e il tempo per esaminare tutti gli archi è (n ), anche se il
a
b
Il peso può essere determinato tramite una funzione di costo
grafo è sparso, ovvero contiene un numero m di archi che è O(n). Se il grafo è pesato, allora
p: V ×V → R, dove R è l’insieme dei numeri reali
nella matrice M si utilizzano i pesi degli archi al posto degli elementi binari. Se puv è il peso
b
✦ Quando tra due vertici non esiste un arco, il peso è infinito
dell’arco (u, v), allora la matrice
M diventa:
nil
2
4
1
2
3
puv ,
se (u, v) ⌅ E,
1
3 nil
muv =
+⇤ (oppure ⇤) se (u, v) ⌅
/ E.
*
3
*
1
2
4
5 nil
1
3
3
4
5
nil
nil
nil
1
© Alberto Montresor
3
4
4
11
4
2
3
1
© Alberto Montresor
4
2
3
9.3 Realizzazioni
con matrici
A
5
Spazio: n2 o n(n+1)/2
7
8
5
6
10
4
5
6
1
*
*
2
3
*
4
*
*
*
3
*
4
*
4
7
*
4
1
*
4
*
8
*
5
*
*
7
8
*
*
6
*
*
*
*
*
*
12
prevedono la possibilità di inserire o cancellare nodi o archi, di ottenere l’insieme di tutti i nodi
Specifica
o dei nodi adiacenti ad un particolare nodo.
Definizioni: Grado
Infine, la situazione può essere ulteriormente complicata dal fatto che in molte applicazioni
ulteriori
informazioni (dette etichette, o pesi) sono associate ai nodi e/o agli archi. In tal caso,
✦ In un grafo non orientato
G RAPH
si parla di grafi etichettati (o pesati), i quali richiedono ulteriori operatori, per reperire e/o ✦ il grado di un vertice è il numero di archi che partono da esso
Graph( )
% Crea un grafo vuoto
modificare le informazioni associate a nodi e/o archi. Non è quindi un caso che nelle librerie
insertNode(N ODE u)
% Aggiunge il nodo u al grafo
dei più diffusi linguaggi di programmazione siano disponibili numerose realizzazioni per✦ leIn un grafo orientato
insertEdge(N ODE u, N ODE v)
% Aggiunge l’arco (u, v) al grafo
strutture di dati insieme, dizionario, etc., ma che non vi sia una realizzazione standard per i ✦ il grado entrante (uscente) di un vertice è il numero di archi incidenti in (da) esso
(N ODE u)
% Rimuove il nodo u dal grafo
grafi. deleteNode
Non staremo
quindi noi ad inventarne una fittizia.
in : 1
deleteEdge
(N ODEsezioni
u, N ODEdiscuteremo
v)
% Rimuove
l’arco (u,
v) realizzare
nel grafo
in : 2
Nelle
prossime
dei principali
approcci
per
la struttura di
out: 1
in : 1
out: 1
2
S
ET
adj
(N
ODE
u)
%
Restituisce
l’insieme
dei
nodi
adiacenti
ad
u
2
dati grafo. Partiremo dall’ipotesi semplificativa che l’insieme statico degli n nodi possa essere
out:
2
3
3
2
S ET V() dai numeri da 1 a n. Per eseguire%un’operazione
Restituisce l’insieme
di tutti
i nodie su tutti gli archi,
rappresentato
su tutti
i nodi
A
1
1
3
utilizzeremo il costrutto foreach come segue:
2
Di tutte u
queste
sole procedure adj() e V();
Complessità
foreach
⌅ G.procedure,
V() do discuteremo la realizzazione delle
il motivo principale è che, in questo testo, i grafi sono usati per rappresentare relazioni tra
✦ O(n+m) liste di adiacenza
foreach v ⌅ G.adj(u) do
oggetti che non mutano nel tempo. In altri termini, non si effettuano mai né cancellazioni né
(u, v)
2 forniti in ingresso, al
inserzioni, fai
né diun’operazione
nodi né di archi, sull’arco
ma si esplorano
sistematicamente
grafi
✦
✦
4
O(n ) matrice di adiacenza
5 2
3
4
in : 2
out: 2
fine di determinarne sottoinsiemi di nodi e/o di archi che soddisfino certe proprietà.
✦
6
“operazioni”
Qualora il grafo sia dinamico, la struttura di dati più adatta O(m)
per rappresentarlo
dipende
0
dalla
particolare
utilizzazione
grafo
stesso.
efficienti
di insiemi
e dizionari
Il primo
foreach
scorre del
tutti
i nodi
delRealizzazioni
grafo, mentre
il secondo
scorre
tutti i nodi 13adiacenti
© Alberto Montresor
© Alberto Montresor
sono state
trattatein
ampiamente
nei capitoliPer
precedenti,
ed il lettore
può cercare
combinar- il termine G.n
al nodo
u preso
considerazione.
semplificare
il testo
inoltre,di useremo
le per esercizio in modo da fornire realizzazioni efficienti per le operazioni di inserzione e
Definizioni:
Cammini
Definizioni: Cicli
come
abbreviazione
di G.V().size().
cancellazione di nodi e/o archi.
Inoltre, nella vita di un programmatore capita di lavorare con i grafi senza menzionarli
✦ esplicitamente.
✦ In un grafo orientato G=(V,E)
In un grafo orientato
Ad esempio,G=(V,E)
un file system può essere visto come un albero di directory e file,
9.3ma laRealizzazioni
con
matrici
presenza di hard link e symbolic link fanno sı̀ che l’albero sia in realtà un grafo. Le ope✦ un cammino di lunghezza k è una sequenza di vertici u , u , ..., u tale che ✦ un ciclo di lunghezza k è un cammino u , u , ..., u tale che 0 1
k
0 1
k
razioni per la creazione di nodi e archi sono quindi condizionate dalla particolare “semantica”
(u
,
u
)
∈
E
per
0
≤
i
≤
k–1
(u
,
u
)
∈
E
per
0
≤
i
≤
k–1,
u
=
u
,
e
k>2
i
i+1
i
i+1
0
k
Uno dei
modiepiù
semplici
per rappresentare
grafo èdibasata
associata
al grafo,
parleremo
di creazione
di file al posto un
di creazione
nodi. su matrici. Dato un grafo
5
6
in : 1
out: 1
in : 0
out: 0
14
G✦ = (V, E), la matrice di adiacenza M = [muv ] (detta anche matrice di adiacenza nodi-nodi)
✦ In un grafo non orientato G=(V,E)
In un grafo non orientato G=(V,E)
è una matrice di dimensione n ⇥ n, tale che:
✦
una catena di lunghezza k è una sequenza di vertici u0, u1, ..., uk tale che [ui, ui+1] ∈ E per 0 ≤ i ≤ k–1
1, se (u, v) ⌅ E,
muv =
2
3
✦
0, Esempio:
se (u, v)1,⌅
/ 2,E.3, 5, 4 è una catena nel grafo con lunghezza 4
Se il grafo non è orientato, la matrice è simmetrica, ovvero muv = mvu per ogni u, v. Un
esempio di matrice di adiacenza è mostrata nella
9.6. (catena) si dice semplice
Un Fig.
cammino
se tutti
i suoi O(1)
verticisesono
distinti
Con questa realizzazione è banale verificare
in tempo
un dato
arco è presente nel
(compaiono una sola volta nella
grafo oppure no. Purtroppo, il tempo per ricavare l’insieme di adiacenza G.adj(u) di un qualsequenza)
4
5
siasi nodo u è sempre
(n), indipendentemente
dalla cardinalità di G.adj(u), perché occorre
scandire un’intera riga della matrice alla ricerca degli elementi muv = 1. Inoltre, lo spazio
di memoria occupato è (n2 ) e il tempo per esaminare tutti gli archi è (n2 ), anche
se il
15
1
© Alberto Montresor
un circuito di lunghezza k è una catena u0, u1, ..., uk tale che [ui, ui+1] ∈ E per 0 ≤ i ≤ k–1, u0 = uk, e k>2
2
3
1
© Alberto Montresor
Esempio: 1, 2, 3, 5, 4, 1 è un circuito con lunghezza 5
Un ciclo (circuito) si dice semplice se
tutti i suoi vertici sono distinti
(tranne ovviamente il primo / l’ultimo)
4
5
16
Definizioni: Grafi aciclici
Definizioni: Grafo completo
Un grafo senza cicli è detto aciclico
✦
2
1
Questo grafo è
aciclico
2
6
Questo grafo non è completo
2
5
2
6
4
Questo grafo non è
aciclico
4
5
5
6
4
3
1
3
3
1
3
1
2
1
Questo grafo è completo
Un grafo orientato aciclico è chiamato DAG (Directed
Acyclic Graph)
3
4
Un grafo completo è un grafo che ha un arco tra ogni coppia di vertici.
✦
4
m=
5
n
X1
i=1
5
17
© Alberto Montresor
Definizioni: Alberi
i=
1)
n(n
2
18
© Alberto Montresor
Definizioni: Alberi di copertura
✦
Un albero libero è un grafo non orientato connesso, aciclico
✦
Se qualche vertice è detto radice, otteniamo un albero radicato
✦
Un insieme di alberi è detta foresta
In un grafo non orientato G=(V, E)
✦
✦
rad
un albero di copertura T è un albero libero T = (V, E′) composto da tutti i nodi di V
e da un sottoinsieme degli archi (E′ ⊆ E), tale per cui tutte le coppie di nodi del
grafo sono connesse da una sola catena nell’albero.
ice
2
2
3
1
2
3
1
1
6
4
5
3
6
4
6
4
5
5
7
8
© Alberto Montresor
9
19
© Alberto Montresor
20
Un esempio di utilizzo dei grafi
✦
Un esempio
di9.utilizzo
CAPITOLO
GRAFI dei grafi
Sherlock Holmes indaga sulla morte del duca McPollock, ucciso da un’esplosione nel suo maniero:
✦
✦
✦
F
Watson: “Ci sono novità, Holmes: pare che il testamento, andato distrutto nell’esplosione, fosse
stato favorevole ad una delle sette ‘amiche’ del duca”
G
H
A
(2) Betty ha incontrato Ann, Charlotte, Edith, Felicia e Helen;
✦
(3) Charlotte ha incontrato Ann, Betty e Edith;
✦
(4) Edith ha incontrato Betty, Charlotte, Felicia;
✦
(5) Felicia ha incontrato Ann, Betty, Edith, Helen;
✦
(6) Georgia ha incontrato Ann e Helen;
✦
(7) Helen ha incontrato Betty, Felicia e Georgia.
G
?
A
?
A
B
H
E
F
G
C
c)
Holmes: “Elementare, Watson: ciò che mi avete detto individua inequivocabilmente l’assassino!”
21
© Alberto Montresor
Figura
9.5: a) Il grafo degli incontri. b) Rappresentazione di circuiti di lunghezza 4 come intersezione di
intervalli della retta. c) Possibili periodi di soggiorno al castello cancellando il nodo A.
Definizione del problema
Watson: “Ho interrogato personalmente le sette donne, ma ciascuna ha giurato di essere stata
nel castello una sola volta nella sua vita. Dagli interrogatori risulta che:
✦
Prendere ispirazione dalla visita degli alberi
✦
(2) Betty ha incontrato Ann, Charlotte, Edith, Felicia e Helen;
Ad esempio:
(1) Ann ha incontrato Betty, Charlotte, Felicia e Georgia;
Dato un grafo G=(V, E) ed un vertice r di V (detto sorgente o radice), visitare ogni vertice raggiungibile nel grafo a partire dal vertice r
(3) Charlotte ha incontrato Ann, Betty e Edith;
visita Betty,
BFS Charlotte,
basata suFelicia;
coda
6 utilizziamo
(4) Edith hauna
incontrato
Ogni nodo deve essere visitato una volta sola
Visita in ampiezza (breadth-first search)
Visita i nodi “espandendo” la frontiera fra nodi scoperti / da scoprire
✦
Esempi: Cammini più brevi da singola sorgente
✦
Esempi: Componenti fortemente connesse, ordinamento topologico
© Alberto Montresor
i “figli”
Q UEUE
S
Queue
Holmes:
“Elementare,
mio caro()
Watson: ciò che mi avete detto individua inequivocabilmente
l’assassino!”
S.enqueue(r)
while not
S.isEmpty
() do
Soluzione
di Holmes:
Il grafo
degli incontri tra le donne, indicate con le loro iniziali, è
disegnatoN
nella
Fig.u9.5a).S.
Sedequeue
ogni donna()
avesse detto la verità, questo grafo dovrebbe rappreODE
sentare l’intersezione di 7 intervalli della retta reale (un grafo siffatto si dice grafo di intervalli).
{ esamina il nodo u }
In tal caso, però, ogni circuito di lunghezza maggiore o uguale a 4 dovrebbe possedere un arco
Sb
Visita i nodi andando il “più lontano possibile” nel grafo
Algoritmi e Strut
visita
(GHolmes,
RAPH G,
ODE r)
Vedete,
che N
le testimonianze
concordano. Ma chi sarà l’assassino?”
Visita in profondità (depth-first search)
✦
(5) Felicia
ha incontrato
Ann, Betty,
Edith,
trattiamo
i “vertici
adiacenti”
come
seHelen;
fossero
(6) Georgia ha incontrato Ann e Helen;
(7) Helen ha incontrato Betty, Felicia e Georgia.
ag
lia
to
✦
22
Visita: attenzione alle soluzioni “facili”
✦
✦
G
B
b)
✦
✦
B
H
Problema: Visita grafi
✦
H
G
(1) Ann ha incontrato Betty, Charlotte, Felicia e Georgia;
© Alberto Montresor
✦
A
B
Vedete, Holmes, che le testimonianze concordano. Ma chi sarà l’assassino?”
✦
C
a)
Watson: “Ho interrogato personalmente le sette donne, ma ciascuna ha giurato di essere stata nel
castello una sola volta nella sua vita.
✦
E
B
H
Holmes: “Ciò che è più strano, è che la bomba sia stata fabbricata appositamente per essere nascosta
nell’armatura della camera da letto, il che fa supporre che l’assassino abbia necessariamente fatto
più di una visita al castello”
✦
✦
149
foreach v ⇥ G.adj(u) do
S.enqueue(v)
23
© Alberto Montresor
24
Esempio di visita - errata
Esempio di visita - errata
r
r
6
u = 6
6
2
2
3
3
9
1
9
1
11
4
5
11
4
10
8
7
5
7
Coda:{6}
Coda:{2,4,9}
25
© Alberto Montresor
Esempio di visita - errata
26
© Alberto Montresor
Esempio di visita - errata
r
r
u = 2
6
u = 4
6
2
2
3
3
9
1
9
1
11
4
5
8
11
4
10
7
5
7
Coda:{4,9,3,1,6}
© Alberto Montresor
10
8
Coda:{9,3,1,6,5,6,1,3}
27
© Alberto Montresor
u
8
10
Nodi già visitati:
u
“Marcare” un nodo visitato in modo che non possa essere visitato di nuovo
u
Bit di marcatura: nel vertice, array separato, etc.
28
Algoritmo
generico per
la visita
Genericamente,
la visita
di un grafo può essere descritta dalla procedura visita(). Visita in ampiezza (breadth first search, BFS)
visita(G RAPH G, N ODE r)
l
Cosa vogliamo fare?
S è l'insieme frontiera
✦ Visitare i nodi a distanze crescenti dalla sorgente
S ET S
Set()
Il funzionamento di S.insert(r)
insert() e remove()
✦ visitare i nodi a distanza k prima di visitare i nodi a distanza k+1
non
è
specificato
{ marca il nodo r come “scoperto” }
✦ Generare un albero BF (breadth-first)
while S.size() > 0 do
CAPITOLO 9. GRAFI
155
✦ albero contenente tutti i vertici raggiungibili da r e tale che il cammino da r N ODE u
S.remove()
ad un nodo nell'albero corrisponde al cammino più breve nel grafo
{ esamina il nodo u }
9.5.1 foreach
Visitav BFS
⇥ G.adj(u) do
✦ Calcolare la distanza minima da s a tutti i vertici raggiungibili
{ esamina l’arco (u, v) }
✦ numero
Nella visita BFS, i nodi sono visitati in ordine di distanza crescente dal nodo di partenza
r,
di archi attraversati per andare da r ad un vertice
if v non è già stato scoperto then
dove la distanza{ da
r adilunnodo
generico
nodo
u è il minimo
numero di archi in un cammino da r
marca
v come
“scoperto”
}
a u. Tale metodo
visita è un’estensione di una visita per livelli di un albero radicato, in cui i
S.di
insert(v)
figli di un nodo sono visitati dopo aver visitato tutti gli altri nodi che stanno allo stesso livello
del padre.
Poiché la BFS visita i nodi più vicini ad r prima di quelli lontani, l’insieme S è una coL’insieme
S contiene
i nodiFIFO).
che sono
stati scoperti,
ma non èancora
visitati.
nodi
vengono
da Montresor
(con politica
di rimozione
Il processo
di marcatura
realizzato
con29I un
vettore
di
© Alberto
© Alberto Montresor
estratti
da
S
uno
dopo
l’altro
per
essere
visitati,
e
nel
contempo
si
esaminano
tutti
gli
archi
booleani; la posizione corrispondente al nodo u viene inizializzata a false, per diventare true
uscenti
nodoviene
visitato
per
scoprire
nuovi
nodi. Grazie al controllo effettuato nel costrutto
if,
Visita
indal
(breadth
first
search,
BFS)
Applicazione
BFS: Numero di Erdös
quando
ilampiezza
nodo
“scoperto”
durante
la visita.
ogni nodo del grafo è inserito nell’insieme S una sola volta; poiché per ogni nodo v, l’insieme
Insieme S gestito
Erdös (1913-1996)
G.bfs
adj(G
(v)RAPH
vieneG,scandito
una sola volta, questo algoritmo tramite
è ottimo
ha complessità ✦(nPaul
+ m).
N ODE r)
unae coda
✦ Matematico
Nel Q
caso
di S
grafi
da
UEUE
⇥ orientati
Queue( )e fortemente connessi, o non orientati e connessi, la visita parte
visitato[v]
corrisponde
un generico
nodo
“passata”. Nei grafi non connessi
odi 1500 articoli, con più di 500 co-autori
✦ Più
S.enqueue
(r) r e raggiunge tutti i nodi in una singolaalla
marcatura del nodo v
nonboolean[
fortemente
connessi,
è necessario
invece
fare più passate, ovvero far ripartire l’algoritmo
] visitato
⇥ new
boolean[1
. . . G.n]
✦ Numero di Erdös
da un
nodo non
precedenza.
foreach
u ⇤ scoperto
G.V() in
{r}
do visitato[u] ⇥ false
✦ Erdös ha erdös = 0
L’ordine
vengono visitati i nodi dipende dalla politica di estrazione della procedura
visitato[r]in⇥cuitrue
✦ I co-autori di Erdös hanno erdös = 1
remove
().not
Esistono
due ()
approcci
principali:
while
S.isEmpty
do
N ODE u ⇥ S.dequeue()
✦ Se X ha scritto una pubblicazione scientifica con – Breadth-First-Search
{ esamina il nodo (uBFS
} ), ovvero visita in ampiezza (detta anche a ventaglio);
un co-autore con erdös = k, ma non con – Depth-First-Search
(DFS
ovvero visita in profondità (detta anche a scandaglio).
foreach v ⇤ G.adj
(u)),do
un co-autore con erdös < k, X ha erdös=k +1
{ esamina l’arco (u, v) }
✦ Chi non è raggiunto da questa definizione ha erdös = +∞
if not visitato[v] then
visitato[v] ⇥ true
S.enqueue(v)
l
30
l
l
© Alberto Montresor
31
© Alberto Montresor
32
Alberto Montresor, erdos = 4
Alan Bertossi, erdos = 3
8
7
3
11
2
6
4
5
9
Figura 9.9: Un grafo non orientato
Come tributo scherzoso alla sua estrema prolificità, è nato il concetto di “numero di Erdős”,
un coefficiente assegnato ad ogni matematico (in generale, ad ogni autore scientifico) per misurare la sua “distanza” da Erdős. Paul Erdős ha erdős = 0. I co-autori dei suoi articoli hanno
erdős = 1. Chi ha scritto articoli in collaborazione con autori con erdős = 1 (ma non con
Erdős) ha erdős = 2. Ricorsivamente, chi ha collaborato con autori con erdős = k (ma non
con autori con erdős < k), ha erdős = k + 1. Chi non è raggiunto da questa definizione,
ha erdős = ⇤. Il massimo numero di Erdős finito è 15; è scherzosamente considerato un
punto d’onore avere un numero di Erdős basso. Gli autori di questo libro hanno erdős 3 e 4,
rispettivamente.
© Alberto Montresor
Dato il grafo delle collaborazioni scientifiche (dove ogni autore è un nodo e due autori sono33
uniti da un arco se e solo se hanno scritto un articolo scientifico insieme), è possibile calcolare
delErdős
numero
di Erdös
ilCalcolo
numero di
di ogni
autore utilizzando una visita in ampiezza a partire da Erdős.
34
© Alberto Montresor
Esempio
0
erdos(G RAPH G, N ODE r, integer[ ] erdős, N ODE[ ] p)
Q UEUE S ⇥ Queue()
S.enqueue(r)
foreach u ⌅ G.V() {r} do erdős[u] = ⇤
erdős[r] ⇥ 0
p[r] ⇥ nil
while not S.isEmpty() do
N ODE u ⇥ S.dequeue()
foreach v ⌅ G.adj(u) do
if erdős[v] = ⇤ then
erdős[v] ⇥ erdős[u] + 1
p[v] ⇥ u
S.enqueue(v)
6
2
3
10
1
11
4
% Esamina l’arco (u, v)
% Il nodo u non è già stato scoperto
5
9
12
7
Coda:{6}
In questa versione della BFS, invece di utilizzare un vettore di booleani per la marcatura,
utilizziamo il vettore erdős di interi, in cui i nodi non ancora scoperti hanno numero di Erdős
pari a ⇤. Il nodo radice (Erdős) viene inizializzato a zero; ogni volta che viene analizzato un35
© Alberto Montresor
© Alberto Montresor
36
Esempio
Esempio
0
0
1
1
6
1
2
3
1
2
3
9
1
1
5
8
11
4
10
5
8
10
7
Coda:{2,4,9}
Coda:{4,9,3,1}
37
© Alberto Montresor
Esempio
38
© Alberto Montresor
Esempio
0
1
0
1
6
1
2
3
2
3
8
2
9
2
11
1
5 2
1
9
1
4
6
2
2
11
1
5 2
4
10
2
7
8
10
7
Coda:{9,3,1,5}
© Alberto Montresor
9
1
7
1
2
2
11
1
4
6
Coda:{3,1,5,8}
39
© Alberto Montresor
40
Esempio
Esempio
0
1
0
1
6
1
2
3
1
2
1
2
3
9
2
1
5 2
2
8
11
5 2
4
10
2
8
10
7
Coda:{1,5,8}
Coda:{5,8}
41
© Alberto Montresor
Esempio
42
© Alberto Montresor
Esempio
0
1
0
1
6
1
2
3
2
3
2
2
3
8
2
9
2
11
1
5
1
9
1
4
6
2
2
11
1
5 2
4
10
2
3
7
8
10
7
Coda:{8,7}
© Alberto Montresor
9
1
7
1
2
2
11
1
4
6
Coda:{7}
43
© Alberto Montresor
44
BFS ,
che è un albero di copertura del grafo G radicato in r. In questo albero, i figli di u
p[r]di⇥
nil(u, v). Il cammino nell’albero che
u sonodei
tutti
i nodi vBFS
scoperti durante la visita
archi
Albero
cammini
while not
S.isEmpty
() ed
doha lunghezza erd
il nodo r ad un nodo s è ovviamente un cammino
anche
nel grafo,
N
ODE
u
⇥
S.
dequeue
()
La procedura
() utilizzata
memorizza
l’albero
deiilcammini
un vettore
✦ La visita
BFS puòerdos
essere
per
ottenere
camminoBFS
piùin
breve
fra duedi padri p
⌅ G.
adj(u)
p[v]
contiene
u se
v è stato scoperto duranteforeach
la visitavdel
nodo
u e do
in particolare de
vertici
(numero
di archi)
if
erdős[v]
=
⇤
then
(u,
v). Il vettore p è passato in input alla procedura erdos(), con ogni elemento del
✦ Albero di copertura di G radicato in r
erdős[v] ⇥ erdős[u] + 1
inizializzato a nil.
✦ Memorizzato tramite vettore dei padri p
⇥ u dal nodo radice r ad u
Una volta ottenuto il vettore p, è possibile ottenere ilp[v]
cammino
✦ Figli di u - nodi v tali che (u,v) ∈ E enqueue
(v)
rico
nodo s tramite la procedura stampaCammino(). SiS.noti
l’interessante
uso della rico
v non è ancora
visitato
che epermette
di stampare
gli
autori nell’ordine atteso da r ad s.
Esempio
0
1
6
1
2
3
2
9
2
1
11
1
5 2
4
2
3
8
10
stampaCammino(N ODE r, N ODE s, N ODE[ ] p)
In questa versione della BFS, invece di u
if r = s then print s
utilizziamo il vettore erdős di interi, in cui i n
else if p[s] = nil then
print “nessun cammino da r apari
s” a ⇤. Il nodo radice (Erdős) viene inizial
7
else
Coda:{}
arco (u, v) e il nodo v non è ancora stato scop
stampaCammino(r, p[s], p)
print s
45
© Alberto Montresor
Algoritmo generico per la visita
✦
Alcune definizioni
✦
✦
✦
✦
✦
Visita in profondità (depth first search, DFS)
9.5.4 Visita DFS
✦
Alcune cose da notare:
L'albero T contiene i vertici visitati
✦
S ⊆ T contiene i vertici aperti:
vertici i cui archi uscenti non sono
ancora stati percorsi
✦
T-S ⊆ T contiene i vertici chiusi:
vertici i cui archi uscenti sono stati
tutti percorsi
✦
✦
I nodi vengono visitati al più una
volta (marcatura)
Tutti i nodi raggiungibili da r
vengono visitati
Ne segue che T contiene
esattamente tutti i nodi
raggiungibili da r
✦
V-T contiene i vertici non visitati
Se u si trova lungo il cammino che
va da r al nodo v, diciamo che:
✦
u è un antenato di v
✦
v è un discendente di u
© Alberto Montresor
46
© Alberto Montresor
✦ Struttura
Visita
in profondità
dati in ordine anticipato di un
Il metodo
di visita DFS è una diretta estensione
delladivisita
158
ordinato.
Quando
viene visitato
un nuovo nodo✦ u,Ricorsione
ci si allontana
da esso
il pila
più possibil
✦ E' spesso
una “subroutine”
della
al posto
di una
un cammino
(o
catena),
visitando
nodi
(e
archi)
nel
cammino
fino
a
giungere
in un “vico
soluzione di altri problemi
esplicita
zato a tutti false). Come negli alberi, è possibile d
co”,
ovvero in un nodo v i cui nodi adiacenti sono già stati tutti visitati. Quando si è rag
✦ Utilizzata per coprire l'intero grafo,
postvisita (riga (2)).
v si torna indietro lungo l’ultimo arco visitato e si riprende la visita allontanandosi lu
non solo i nodi raggiungibili da
altro cammino di nodi (e archi) non ancoradfs
visitati.
Questa
tecnica
di visita] visitato)
è un’applic
(G RAPH
G, N ODE
u, boolean[
una singola sorgente della(diversamente
tecnica backtrack
[cfr.
§
16].
da BFS)
visitato[u]
true
Nella visita DFS, l’insieme S potrebbe
realizzato
una pila} (politic
(1) essere
{ esamina
il nodo utramite
(caso previsita)
Output
mozione LIFO). È più naturale invece descrivere
l’algoritmo
in do
maniera ricorsiva, in
foreach
v ⇥ G.adj(u)
{
esamina
l’arco
(u,
✦ Invece
più
coinciso
di ed
un elegante.
albero, unaL’algoritmo
foresta DF viene richiamato su un grafov)G a partire da un n
if not visitato[v]
mentre
visitato èGilπ=(V,
vettore
l’insiemethen
di nodi già scoperti (in
(depth-first)
Eπ)di booleani che rappresenta
✦
47
© Alberto Montresor
Contenente un insieme di
alberi DF
dfs(G, v, visitato)
(2)
{ esamina il nodo u (caso postvisita) }
Esempio 9.14 (DFS). L’ordine di visita DFS dei n
mendo di partire dal nodo r = 1 e che gli insiemi
a
48
Componenti connesse e fortemente connesse
u
u
Definizioni: Raggiungibilità
Terminologia
✦
u
Componenti connesse (connected components, CC)
u
Componenti fortemente connesse (strongly connected components, SCC)
In grafo orientato (non orientato)
✦
1 è raggiungibile da 4 e
viceversa
Motivazioni
u
u
u
Se esiste un cammino (catena) c tra i vertici u e v, si dice che v è raggiungibile da
u tramite c
Molti algoritmi che operano sui grafi iniziano decomponendo
il grafo nelle sue componenti
L'algoritmo viene poi eseguito su ognuna delle componenti
2
I risultati vengono poi ricomposti assieme
49
© Alberto Montresor
Definizioni: Grafi connessi e componenti connesse
✦
✦
1
✦
G è connesso ⇔ esiste un cammino da ogni vertice ad ogni altro vertice
Un grafo G′ = (V′, E′) è una componente connessa di G ⇔ è un sottografo di G connesso e massimale
✦
G′ è massimale ⇔ non esiste un sottografo G′′ di G che sia connesso e “più grande” di G′, ovvero tale per cui G′ ⊆ G′′ ⊆ G
✦
✦
Verificare se un grafo non orientato è connesso
✦
Identificare le componenti connesse di cui è composto
5
50
Soluzione
✦
2
3
A
1
✦
4
Un grafo è connesso se, al termine della DFS, tutti i nodi sono stati marcati
Altrimenti, una singola passata non è sufficiente e la visita deve ripartire da un nodo non marcato, scoprendo una nuova porzione del grafo
Strutture dati
✦
Vettore id degli identificatori di componente
✦
id[u] è l’identificatore della componente connessa a cui appartiene u
5
6
© Alberto Montresor
4
Problema
✦
G′ è un sottografo di G (G′ ⊆ G) se e solo se V′ ⊆ V e E′ ⊆ E
5
© Alberto Montresor
Definizioni
✦
3
Applicazioni DFS: Componente connesse
In un grafo non orientato G
✦
2
3
1
4
✦
4 è raggiungibile da 1 ma non
viceversa
51
© Alberto Montresor
52
Componenti connesse
Componenti connesse
1
integer[ ] cc(G RAPH G, S TACK S)
integer[ ] id
new integer[1 . . . G.n]
foreach u 2 G.V() do id[u]
0
integer counter
0
while not S.isEmpty() do
u
S.pop()
if id[u] = 0 then
counter
counter + 1
ccdfs(G, counter , u, id)
2
1
1
53
Alberi di copertura DFS
✦
✦
se l’arco è esaminato passando da un nodo di T ad un suo discendente (che non sia figlio) in T è detto arco in avanti
2
9
3
11
2
CAPITOLO 9. GRAFI
1
2
3
4
Variabili globali
✦
time orologio
✦
dt
discovery time
✦
ft
finish time
1
2
4
altrimenti, è detto arco di attraversamento
3
© Alberto Montresor
7
© Alberto Montresor
✦
Tutte le volte che viene incontrato un arco che connette un nodo marcato ad uno
non marcato, esso viene inserito nell’albero T
se l’arco è esaminato passando da un nodo di T ad un altro nodo che è suo antenato in
12T , è detto arco all’indietro
3
54
Schema DFS
La visita DFS genera l’albero (foresta) dei cammini DFS
✦
6
8
© Alberto Montresor
Gli archi non inclusi in T possono essere divisi in tre categorie durante la visita:
5
2
4
id[u]
counter
foreach v 2 G.adj(u) do
if id[v] = 0 then
ccdfs(G, counter , v, id)
✦
2
10
ccdfs(G RAPH G, integer counter , N ODE u, integer[ ] id)
✦
2
1
return id
✦
3
1
dfs-schema(G RAPH G, N ODE u)
esamina il nodo u prima (caso pre-visita)
time
time + 1; dt[u]
time
foreach v ⇥ G.adj(u) do
esamina l’arco (u, v) di qualsiasi tipo
if dt[v] = 0 then
esamina l’arco (u, v) in T
dfs-schema(g, v)
else if dt[u] > dt[v] and ft[v] = 0 then
esamina l’arco (u, v) all’indietro
else if dt[u] < dt[v] and ft[v] ⇤= 0 then
esamina l’arco (u, v) in avanti
else
esamina l’arco (u, v) di attraversamento
esamina il nodo u dopo (caso post-visita)
time
time + 1; ft[u]
time
55
© Alberto Montresor
56
CAPITOLO 9. GRAFI
Esempio
[1, ]
B
C
A
D
[1, ]
dfs-schema(G RAPH G, N ODE u)
F
E
G
161
Esempio
F
[2, ]
esamina il nodo u prima (caso pre-visita)
time
time + 1; dt[u]
time
foreach v ⇥ G.adj(u) do
I esamina l’arco (u, v) di qualsiasi tipo
if dt[v] = 0 then
esamina l’arco
M (u, v) in T
dfs-schema(g, v)
else if dt[u] > dt[v] and ft[v] = 0 then
esamina l’arco (u, v) all’indietro
else if dt[u] < dt[v]Land ft[v] ⇤= 0 then
H esamina l’arco (u, v) in avanti
else
esamina l’arco (u, v) di attraversamento
B
I
A
M
D
E
L
H
G
esamina il nodo u dopo (caso post-visita)
time
time + 1; ft[u]
time
è un antenato di u, allora esiste un cammino da v a u e un arco da u a v, ovvero un ciclo. Se
esiste un ciclo, sia v il primo nodo del ciclo che viene visitato e sia (u, v) un arco del ciclo. Il
cammino che connette v ad u verrà prima o poi visitato,
verrà scoperto l’arco all’indietro
57 e da u
© Alberto Montresor
(u, v). Adattando opportunamente lo schema di cui sopra, si ottiene l’algoritmo ciclico() che
restituisce true se il grafo contiene un ciclo. Si noti che non appena viene trovato un arco
Esempio
all’indietro, la procedura termina restituendo true e chiudendo tutte le chiamate ricorsive.
© Alberto Montresor
Esempio
[1, ]
58
[1, ]
F
[2, ]
[3, ]
C
B
C
A
D
E
G
boolean ciclico(G RAPH G, N ODE u)
time
time + 1; dt[u]
time
foreach v ⇥ G.adj(u) do
I if dt[v] = 0 then
if ciclico(G, v) then return true
else if dt[u] > dt[v] and ft[v] = 0 then
return trueM
time
time + 1; ft[u]
return false;
H
F
[2, ]
B
[3, ]
C
I
A
M
time
[4, ]
L
9.5.8 Applicazione schema
DFS :
D
E
H
L
componenti fortemente connesse G
In alcuni casi, è possibile dimostrare la correttezza di un algoritmo in base ai tempi di inizio e di fine, senza doverli effettivamente calcolare! Come esempio, si consideri il problema
© Alberto Montresor
59
© Alberto Montresor
60
Esempio
Esempio
[1, ]
[1, ]
F
[2, ]
B
[3, ]
F
[2, ]
C
B
[3, ]
I
A
C
I
[6, ]
A
M
[4, ]
M
[5, ]
[5, ]
D
E
[4, ]
L
H
D
G
61
Esempio
62
© Alberto Montresor
Esempio
[1, ]
[1, ]
F
[2, ]
B
F
[2, ]
C
B
[3, ]
I
[6, 7]
A
C
I
[6, 7]
A
M
M
[5, ]
[4, ]
D
E
[5, ]
H
[4, ]
L
G
© Alberto Montresor
L
H
G
© Alberto Montresor
[3, ]
E
D
E
G
63
© Alberto Montresor
H
L
[8, ]
64
Esempio
Esempio
[1, ]
[1, ]
F
[2, ]
B
[3, ]
F
[2, ]
C
[6, 7]
A
B
[3, ]
I
C
I
[6, 7]
A
[10, ]
M
M
[5, ]
[4, ]
D
E
[5, ]
[4, ]
L
H
D
E
[9, ]
G
[9, ]
G
[8, ]
65
© Alberto Montresor
Esempio
[8, ]
66
© Alberto Montresor
Esempio
[2, 17]
B
[3, 16]
L
H
[1, 18]
F
C
[3, 16]
I
[6, 7]
A
[2, 17]
B
[1, 18]
F
C
I
[6, 7]
A
[10, 11]
[10, 11]
M
M
[5,14 ]
[4,15]
D
E
[5,14 ]
H
[4,15]
L
D
E
[9, 12]
G
© Alberto Montresor
[19, ]
H
L
[9, 12]
G
[8, 13]
67
© Alberto Montresor
[8, 13]
68
Esempio
Esempio
[2, 17]
B
[3, 16]
[1, 18]
F
C
[3, 16]
CAPITOLO 9. GRAFI
I
[6, 7]
A
[10, 11]
M
D
E
L
[8, 13]
69
© Alberto Montresor
CAPITOLO 9. GRAFI
M
[19, 22]
[20, 21]
L
H
[9, 12]
70
161esamina il nodo u dopo (caso post-visita)
time + 1; ft[u]
time
Classificazione degli archi
dfs-schema(G RAPH G, N ODE u)
u
[6, 7]
D
u
[1, 8]
A
E
[9, 10]
Cosa serve la classificazione?
è un antenato di u, allora esiste un cammino da v a u e un arco da u a v, ovvero un ciclo. Se
E' possibile
dimostrare
proprietà,
che e sia (u, v) un arco del ciclo. Il
esiste uun ciclo,
sia v il primo
nodo delalcune
ciclo che
viene visitato
cammino che
u verrà
prima o negli
poi visitato,
e da u verrà scoperto l’arco all’indietro
poi connette
possonov ad
essere
sfruttate
algoritmi
(u, v). Adattando opportunamente lo schema di cui sopra, si ottiene l’algoritmo ciclico() che
u
restituisce
true se il grafo contiene un ciclo. Si noti che non appena viene trovato un arco
Esempio:
all’indietro, la procedura termina restituendo true e chiudendo tutte le chiamate ricorsive.
[2, 5]
B
DAG non hanno archi all'indietro (dimostrare)
boolean ciclico(G RAPH G, N ODE u)
time
time + 1; dt[u]
time
foreach v ⇥ G.adj(u) do
if dt[v] = 0 then
if ciclico(G, v) then return true
else if dt[u] > dt[v] and ft[v] = 0 then
return true
C
[3, 4]
time
time + 1; ft[u]
return false;
esamina il nodo u dopo (caso post-visita)
time
time + 1; ft[u]
time
© Alberto Montresor
[10, 11]
© Alberto Montresor
time
Classificazione degli archi
esamina il nodo u prima (caso pre-visita)
time
time + 1; dt[u]
time
foreach v ⇥ G.adj(u) do
esamina l’arco (u, v) di qualsiasi tipo
if dt[v] = 0 then
esamina l’arco (u, v) in T
dfs-schema(g, v)
else if dt[u] > dt[v] and ft[v] = 0 then
esamina l’arco (u, v) all’indietro
else if dt[u] < dt[v] and ft[v] ⇤= 0 then
esamina l’arco (u, v) in avanti
else
esamina l’arco (u, v) di attraversamento
[6, 7]
[5,14 ]
esamina il nodo u prima (caso pre-visita)
time
time +D1; dt[u]
time
E
[4,15]
foreach v ⇥ G.adj(u) do
esamina l’arco (u, v) di qualsiasi tipo
if dt[v] = 0 then
esamina l’arco (u,Gv) in T
[8, 13]
dfs-schema(g, v)
else if dt[u] > dt[v] and ft[v] = 0 then
esamina l’arco (u, v) all’indietro
else if dt[u] < dt[v] and ft[v] ⇤= 0 then
esamina l’arco (u, v) in avanti
else
esamina l’arco (u, v) di attraversamento
[9, 12]
G
161
I
dfs-schema(G RAPH G, N ODE u)
[20, ]
H
C
A
[19, ]
[5,14 ]
[4,15]
[2, 17]
B
[1, 18]
F
71
© Alberto Montresor
Variabile time:
✦
variabile globale, oppure
✦
passata per riferimento)
time
72
Ordinamento topologico
✦
Ordinamento topologico
Dato un DAG G (direct acyclic graph), un ordinamento topologico su G è un
ordinamento lineare dei suoi vertici tale per cui:
✦
✦
✦
Problema:
✦
se G contiene l’arco (u,v), allora u compare prima di v nell’ordinamento
Per transitività, ne consegue che se v è raggiungibile da u, allora u compare prima di v nell'ordinamento
✦
Soluzioni
✦
✦
Nota: possono esserci più ordinamenti topologici
2
1
1
3
3
5
2
✦
1
2
3
4
73
Soluzione diretta
2
4
3
5
2
4
5
✦
74
Algoritmo
4
✦
5
✦
Output:
Output: 1
Output: 1 3
✦
2
4
✦
Output: 1 3 5 2
[2,5]
Si effettua una DFS
L’operazione di visita consiste nell’aggiungere il vertice alla testa di una lista“at finish time”
2
[1, 10]
1
[6,9]
3
Si restituisce la lista di vertici
Output
✦
4
© Alberto Montresor
Esercizio: scrivere lo pseudocodice per questo algoritmo
Qual è la complessità?
l con matrici di adiacenza
l con liste di adiacenza
© Alberto Montresor
✦
Output: 1 3 5
Trovare un vertice che non abbia alcun arco
incidente in ingresso
Stampare questo vertice e rimuoverlo, insieme ai suoi archi
Ripetere la procedura finché tutti i vertici
risultano rimossi
Ordinamento topologico basato su DFS
2
3
Basata su DFS
5
© Alberto Montresor
1
Diretta
(INEFFICIENTE!)
4
4
5
Fornire un algoritmo che dato un grafo orientato aciclico, ritorni un ordinamento
topologico
[3,4]
4
[7,8]
5
Sequenza ordinata di vertici,
in ordine inverso di finish time
Perché funziona?
Output: 1 3 5 2 4
75
© Alberto Montresor
76
12
Algoritmi e Strutture
di Dati
inverso
di tempo di fine.
Ordinamento topologico basato su DFS - Liste
Ordinamento topologico basato su DFS - Stack
topSort(G RAPH G, S TACK S)
topSort(G RAPH G, S EQUENCE L)
boolean[ ] visitato
boolean[1 . . . G.n]
foreach u 2 G.V() do visitato[u]
false
foreach u 2 G.V() do
if not visitato[u] then
ts-dfs(G, u, visitato, L)
boolean[ ] visitato
boolean[1 . . . G.n]
foreach u 2 G.V() do visitato[u]
false
foreach u 2 G.V() do
if not visitato[u] then
ts-dfs(G, u, visitato, S)
ts-dfs(G RAPH G, N ODE u, boolean[ ] visitato, S EQUENCE L)
ts-dfs(G RAPH G, N ODE u, boolean[ ] visitato, S TACK S)
visitato[u]
true
foreach v 2 G.adj(u) do
if not visitato[v] then
ts-dfs(G, v, visitato, L)
visitato[u]
true
foreach v 2 G.adj(u) do
if not visitato[v] then
ts-dfs(G, v, visitato, S)
L.insert(L.head(), u)
S.push(u)
77
© Alberto Montresor
Definizioni: Grafi fortemente connessi e componenti fortemente connesse
T REE lookupNode(T REE T, I TEM x)
✦
Soluzione errata
InifunTgrafo
G 6= x then
6= nilorientato
and T.key
return
lookupNode
iif(x
< T.key,
T.left,
x)ad ogni altro
✦ G è
fortemente
connesso(⇔
esiste
un cammino
da T.right),
ogni vertice
✦
✦
Proviamo ad utilizzare l’algoritmo per le componenti connesse?
✦
else
vertice
return T
✦
78
© Alberto Montresor
Risultati variano a seconda del nodo da cui si parte
Un grafo G′ = (V′, E′) è una componente fortemente connessa di G ⇔ è un sottografo di G fortemente connesso e massimale
Definizioni
✦
✦
G′ è un sottografo di G (G′ ⊆ G) se e solo se V′ ⊆ V e E′ ⊆ E
1
G′ è massimale ⇔ non esiste un sottografo G′′ di G che sia fortemente connesso e “più grande” di G′, ovvero tale per cui G′ ⊆ G′′ ⊆ G.
© Alberto Montresor
2
2
3
1
3
6
6
4
4
5
5
79
© Alberto Montresor
80
Componenti fortemente connesse
✦
Componenti fortemente connesse - algoritmo
Algoritmo (Kosaraju, 1978)
integer[ ] scc(G RAPH G)
1. Effettua una DFS di G
2. Calcola il grafo trasposto
GT
CAPITOLO 9. GRAFI
S TACK S
topSort(G)
163
% Prima visita
G RAPH GT
Graph()
% Calcolo grafo trasposto
T
T
foreach
u
2
G.
V
()
do
G
.
insertNode
(u)
3. Effettua una DFS di G esaminando i vertici in ordine inverso di tempo di fine
Questo algoritmo si basa sull’osservazione
foreach u 2 G.che
V() un
do grafo G e il suo trasposto GT hanno le
4. Fornisci i vertici di ogni albero della foresta depth-first prodotta
al passo
3. stesse
componenti
fortemente connesse;
questo
dalla
foreach
v 2 deriva
G.adj(u)
do simmetria della definizione, che
come una diversa SCC
GTalmeno
.insertEdge
(v, u) da u a v e almeno un cammino
prevede per ogni coppia di nodi u, v, esista
un cammino
✦
✦
✦
da v ad u.
return cc(GT , S)
Per capire il funzionamento, è utile considerare il grafo delle componenti Gc = (V c , E c )
Dato un grafo G = (V, E), il grafo trasposto GT = (V, ET ) è formato
dagli
del grafo
G, stessi
definito come segue:
nodi, mentre gli archi hanno direzioni invertite: i.e,
– V c = {C1 , C2 , . . . , Ck }, dove Ci è l’i-esima componente connessa di G;
✦ ET ={(u,v) | (v,u) ∈ E}
– E c = {(Ci , Cj ) : ⇤(ui , uj ) ⇥ E : ui ⇥ Ci , uj ⇥ Cj }
Grafo trasposto
Costo computazione
% Seconda visita
In altre parole, le componenti sono “contratte” in un singolo vertice, e cosı̀ gli archi che
vanno da componente a componente. È facile notare che il grafo delle componenti è aciclico:
se cosı̀ non fosse, i nodi delle componenti appartenenti al ciclo potrebbero tutti raggiungersi
81
82
© Alberto Montresor
© Alberto Montresor
l’uno con l’altro; farebbero
quindi
parte di un’unica componente, una contraddizione.
Estendiamo il concetto di tempo di inizio e di fine per le componenti. Sia C un sottoinComponenti fortemente connesse - dimostrazione correttezza sieme di V ; definiamo dt(C) come
Componenti
connesse
- dimostrazione
il primo fortemente
tempo di scoperta
e ft(C)
come l’ultimocorrettezza
tempo di
completamento:
✦ Si può estendere il concetto di dt e ft al grafo delle componenti
✦ Domanda
dt(C) = min{dt[u] : u ⇥ C}
✦ Che rapporto c’è fra le SCC del grafo G e del suo trasposto GT ?
ft(C) = max{ft[u] : u ⇥ C}
✦ Grafo delle componenti Gc = (Vc, Ec)
✦ Teorema
In base a queste definizioni, possiamo
enunciare il seguente teorema.
✦ Vc = {C , C , . . . , C }, dove C è l’i-esima componente fortemente connessa di G;
1
2
k
i
✦ Siano C e C′ due componenti distinte nel grafo orientato G = (V, E). 13
Teorema 9.1. Siano C e C due componenti distinte nel grafo orientato G = (V,
E). Suppo✦ Ec ={(C ,C ): ∃(u ,u ) ∈ E :u ∈C ,u ∈C }
Supponiamo
che
esista
un
arco
(C,
C′)
∈ Ec. Allora ft(C) > ft(C′)
i j
i j
i
i j
j
niamo che esista un arco (C, C ) ⇥ E c . Allora ft(C) > ft(C ).
[9,10]
[2,11]
Il grafo delle
[1, 8]
Dimostrazione Sono dati due casi: viene scoperto prima un
nodo di C
oppure di C . 2
[11,12]
2
[3,10]
componenti
[1,12]
2
3
3
è aciclico?
• dt(C) < dt(C ): sia x il primo nodo scoperto in C, all’istante
dt(C). Poiché tutti i nodi
3
1
1
in C ⌅ C sono raggiungibili
1 da x e non ancora scoperti, la loro visita inizierà e terminerà
6
[5,7] 6
prima della terminazione della visita di x. Quindi ft(C) = ft(x) > ft(y), per ogni y ⇥
6
[7,8]
C ⌅C
{x}. Ne segue che ft(C) > ft(C ).
[2, 7]
[4,9]
4
4
5
5
• dt(C ) < dt(C): sia y il primo nodo scoperto
in C ; tutti i nodi in C sono raggiungibili
4
5
da y e non ancora scoperti. Al termine della visita di tutti i nodi di C , tutti i nodi[3,4]
di C
[5,6]
non
sono
ancora
stati
scoperti.
La
loro
visita
inizierà
e
terminerà
dopo
ft(C
);
ne
segue
che
83
84
© Alberto Montresor
© Alberto Montresor
✦
O(m+n)
ro quindi parte di un’unica componente, una contraddizione.
to diComponenti
tempo di inizio
e di fine per
le componenti.
Sia C un
sottoinfortemente
connesse
- dimostrazione
correttezza
(C) come il primo tempo di scoperta e ft(C) come l’ultimo tempo di
✦
Componenti fortemente connesse
Si può estendere il concetto di dt e ft al grafo delle componenti
✦
dt(C) = min{dt[u] : u ⇥ C}
✦
ft(C) = max{ft[u] : u ⇥ C}
✦ Corollario
ni, possiamo
enunciare il seguente teorema.
✦ Siano C e C′ due componenti distinte nel grafo orientato G = (V, E). SuppoC due componenti distinte nel grafo orientato
G = (V, E). Supponiamo
che esista un arco (u,v) ∈ ET con u ∈ C, v ∈ C′. Allora ft(C) < ft(C′)
c
(C, C ) ⇥ E . Allora ft(C) > ft(C ).
✦ (u,v) ∈ ET ⇒
[9,10]
[1, 8]
(v,u)scoperto
∈ E ⇒
prima un nodo di C oppure di C .
ati due casi: viene
2
[11,12]
c
(C’, C) ∈ E ⇒
3
x il primo nodo
scoperto
ft(C)
< ft(C′)
in C, all’istante dt(C). Poiché tutti i nodi
1 inizierà e terminerà
ungibili da x e non ancora scoperti, la loro visita
6
ione della visita di x. Quindi ft(C) = ft(x) > ft(y), per ogni y ⇥
gue che ft(C) > ft(C ).
[2, 7]
4
y il primo nodo scoperto in C ; tutti i nodi in C sono raggiungibili
operti. Al termine della visita di tutti i nodi di C , tutti
i nodi di C
[3,4]
© Alberto Montresor
scoperti. La loro visita inizierà e terminerà dopo ft(C ); ne segue che
essere letto nel modo seguente: l’ultimo nodo della componente C
errà inserito nella pila prima dell’ultimo nodo della componente C .
otata in ordine inverso, questo significa che almeno un nodo di C
odi di C nella seconda visita. A questo punto entra in gioco il grafo
c
un arco che connette C con C , in GT esso è sostituito da un arco
ella seconda visita quindi i nodi di C non sono raggiungibili dai nodi
viduata come una componente a sé stante.
Algoritmo di Tarjan (1972)
✦
✦
Tarjan, R. E. "Depth-first search and linear graph algorithms", SIAM Journal on
Computing 1(2): 146–160 (1972)
Algoritmo con costo O(m+n) come Kosaraju
E’ preferito a Kosaraju in quanto necessita di una sola visita e non richiede il
grafo trasposto
[5,7]
5
© Alberto Montresor
86