I grafi sono formati da due insiemi:
- Insieme dei vertici (
$V$ ) - Insieme degli archi (
$E$ )
è formato da coppie di vertici, se le coppie sono ordinate il grafo è orientato, se non sono ordinate il grafo è non orientato.
è un grafo che ha come vertici due insiemi separati, i cui archi collegano solo i vertici di un insieme ai vertici dell'altro, non ci sono collegamenti tra vertici dello stesso sottoinsieme.
![[Grafo bipartito.svg|center]]
$$R=\{1,2,3\}$$
$$L=\{4,5,6\}$$
$$V=\{L\cup R\}$$
$$E=\{(1,4), (4,3), (2,5)\}$$
I grafi a maglia possono essere impostati come dei grafi bipartiti
![[Grafo Mesh.svg|center small]]
![[Grafo frutteto.svg|center small]]
![[Grafo rete.svg|center small]]
$$|E|=m \begin{cases}
|E| \leq \frac {n(n-1)}2 & \text{grafo non orientato}\
|E| \leq n(n-1) & \text{grafo orientato}
\end{cases}$$
Un grafo connesso (con tutti i vertici connessi tra loro) non orientato di dimensione
Un grafo non orientato si definisce connesso se:
title: Path ($\sim$)
Sequenza di vertici tale che ogni coppia di vertici consecutivi è legato da un arco
Un grafo connesso che ha il minor numero di archi possibile (
![[Pasted image 20230301101608.png| center]]
Se G ha
1 | 2 | 3 | 4 | |
---|---|---|---|---|
1 | 0 | 1 | 1 | 0 |
2 | 1 | 0 | 1 | 0 |
3 | 1 | 1 | 0 | 1 |
4 | 0 | 0 | 1 | 0 |
|i |
Sono 2 vettori:
-
$V$ che contiene la posizione in E in cui è presente il primo arco uscente. -
$E$ in cui sono scritti quali archi sono collegati
V | E | |
---|---|---|
1 | 1 | 2 |
2 | 3 | 3 |
3 | 4 | 3 |
4 | - | 4 |
A seconda se il grafo sia sparso oppure denso la complessità del suo arco varia da
L'[[#Esplorare Un Grafo|esplorazione]] di un grafo impiega
- Lista delle adiacenze, vettore delle adiacenze
$O(V+E)$ - Matrice delle adiacenze
$O(v^2)$
Dato un grafo connesso, esplorare G vuol dire estrarre un albero che esplora tutti i vertici di G
Depth First Search visit.
La DFS-visit è preceduta dalla procedura di inizializzazione
initialize(G=(V,E))
for v in V:
color(v) = white
P(v) = nil
L'obiettivo è quello di estrarre un albero di copertura radicato nel primo vertice passato alla procedura dfs-visit.
un vertice è:
- white: non visitato
- gray: visitato
- black: visitati tutti gli archi
Ogni volta che tocco un vertice mai esplorato vado a esplorare tutti i vertici a esso connessi.
dfs-visit(G=(V.E), u):
color(u) = grey
for v in Adj(u):
if color(v) == white:
(u,v) in T
P(v) = u
dfs-visit(G, v)
else if v != P(u):
(u,v) in B
else
(u, v) in T
color(u) = black
- Un grafo connesso è un __albero__ se _non esistono_ archi in $B$.
- Un grafo connesso ha un __ciclo__ se _esistono_ archi in $B$
Esplorazione di
dfs-visit(G, v, time):
color(v) = gray
time++;
d(v) = time
for u in adj(v):
if color(u) == white:
(v, u) in T
P(u) = v
dfs-visit(G, u, time)
if color(u) == gray:
(v, u) in Backward
if color(u) == black and d(v) < d(u):
(v, u) in Forward
else:
(v, u) in Cross
color(v) == black
time++
f(v) = time
- Un grafo connesso _senza cicli_ (senza archi Backward) si dice __Direct Acyclic Graph (DAG)__
- Un grafo orientato aciclico ha almeno un __sink__, cioè un vertice senza archi uscenti.
è un ordinamento dei vertici:
sort-topologico(G, DAG)
1. DFS(G)
2. inserisci in testa ad una lista L ogni nodo che diventa nero
3. return L
repeat
sia s un sink in G
title: Chiedere a qualcuno slide 12 [[topologicalsort-sinkuniversale-cfc.pdf]]
Cercare un sink in G rappresentato dalla matrice delle adiacenze
```ad-check
title: Soluzione semplice (non cerca sink universale)
t = 1
trovato = false
while t < |v| and !trovato:
if !sink-test(G, t):
t++
else:
trovato = true
return t
sink-test(G, t):
j = 1
while j <= n and G[t, j] == 0:
j++
return !(G[t, j] == 1)
La complessità nel worst case è $O(v^2)$
```
trova-sink-universale(G):
i = 1
j = 1
while (j <= n) && (i <= n):
if G[i, j] == 1:
i++
else:
j++
if i > n:
return false
else:
return test-sink(G, j)
L'algoritmo appena proposto non funziona per trovare un sink singolo, ma solo per il sink universale
Per ogni coppia
![[fortemente_connesso.svg|center]]
dfs(G = (V, E)):
for v in V:
color(v) = white
P(v) = nil
num_cc = 0
for i = 1:|v|
if color(i) == white:
num_cc++
dfs-visit(G, i)
num_cc indica le componenti connesse
Basta chiamare dfs-visit passando il valore di num_cc
dfs-visit(G, i, num_cc)
![[esempio.svg|center]]
| | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| color| g | g | g | g | g | g|
|P| nil | 1 | 2 | 3 | 4 | 5 |
|
```ad-missing
title: Riguardare 15-03-2023
i vertici sono componenti fortemente connesse e gli archi sono gli archi del grafo originario che vanno da una CFC a un'altra.
Una componente fortemente connessa corrisponde a un albero i cui vertici sono i vertici della componente fortemente connessa
Per disegnare questo grafo mi interessano solo gli archi che vanno da vertici appartenenti a componenti fortemente connesse
title: supponiamo per assurdo che il grafo non sia un DAG
![[non_dag.svg|center]]
```ad-failure
Esistendo l'arco B, l'intero grafo diventa una componente fortemente connessa, non rendendolo più un $G^{scc}$
```
Grafo con tutti gli archi invertiti
![[trasposto.svg|center]]
ho trovato un albero radicato in 1
1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|
color | g | g | g | g | g | g |
P | nil | 3 | 1 | nil | nil | nil |
d | 1 | 3 | 2 | 7 | 9 | 11 |
h | 6 | 4 | 5 | 8 | 10 | 12 |
Se calcolo il traspoto di un $G^{scc}$ ottengo un __reverse sort topologico__
Vogliamo calcolare il grafo
Il calcolo di
square( G ):
for r = 1:n
for c = 1:n
G2[r, c] = 0
for k = 1:n
if G[r, k] and G[k, c]:
G2[r, c] = G2[r, c] or 1
title: Calcolo di $G^*$
$$G^* = G \cup G^2$$
g-star( G ):
G2 = square(G)
for r = 1:n
for c = 1:n
GS[r, c] = G[r, c] or G2[r, c]
star(G, GS):
for r = 1:n
for c = 1:n
GS[r, c] = G[r, c]
for k = 1:n
if G[r, k] and G[k, c]:
GS = GS[r, c] or 1
BFS(G, s * source):
for v in V:
color(v) = white
P(v) = nil
color(s) = gray
EnQueue(Q, s)
while (Q not empty):
u = DeQueue(Q)
for v in adj(u):
if color(v) = white:
(u, v) in T
P(v) = u
color(v) = gray
EnQueue(Q, v)
color(u) = black
collapse: open
title: Calcolo della distanza
BFS(G, s * source):
for v in V:
color(v) = white
P(v) = nil
d(v) = infinite
color(s) = gray
d(s) = 0
EnQueue(Q, s)
while (Q not empty):
u = DeQueue(Q)
for v in adj(u):
if color(v) = white:
(u, v) in T
P(v) = u
color(v) = gray
d(v) = d(u) + 1
EnQueue(Q, v)
color(u) = black
- d(v) dipende solo dalla sorgente
- d è __crescente__
- BFS(G, s) visita ogni vertice raggiungibile da s
- dist(s, s) = 0 = d(s)
- dist(s, v) = distanza da s a v = d(v)
Dato un grafo G trovare il diametro di Gs
![[Untitled Diagram 1.svg|center]]
for u,v in VxV:
if d(u, v) > max:
max = d(u, v)
return max
Gli archi sono denominati rispetto alla loro posizione dei loro estremi in T:
- back verso antenato
- forward verso discendente
- cross altrimenti
Nel grafo non orientato esistono solo gli archi cross
visit(G, L, v):
C = 0
u in v
while dist(u) > 0:
let w in adj(u)
delete (u, w)
dist(u)--
add u to C
if dist(u) > 0:
add u to L
u = w
return C
euler-tour(G):
T = 0
L = any vertex in G
while L != 0:
remove v from L
C = visit(G, L, v)
if location in T = nil:
T in C
else:
introduce C before locator of v in T