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