Componenti fortemente connessi (SCC) sono un concetto fondamentale nella teoria dei grafi e negli algoritmi. In un grafo orientato, a Componente fortemente connesso è un sottoinsieme di vertici in cui ogni vertice del sottoinsieme è raggiungibile da ogni altro vertice dello stesso sottoinsieme attraversando i bordi diretti. Trovare il SCC di un grafico può fornire importanti informazioni sulla struttura e sulla connettività del grafico, con applicazioni in vari campi come analisi dei social network, scansione web e routing della rete . Questo tutorial esplorerà la definizione, le proprietà e gli algoritmi efficienti per identificare i componenti fortemente connessi nelle strutture dati dei grafici
multithreading in Java
Tabella dei contenuti
- Cosa sono i Componenti Fortemente Connessi (SCC)?
- Perché i componenti fortemente connessi (SCC) sono importanti?
- Differenza tra componenti connessi e fortemente connessi (SCC)
- Perché il metodo DFS convenzionale non può essere utilizzato per trovare componenti fortemente connessi?
- Collegamento di due componenti fortemente connessi tramite un bordo unidirezionale
- Approccio della forza bruta per trovare componenti fortemente connessi
- Approccio efficiente per trovare componenti fortemente connessi (SCC)
- Conclusione
Cosa sono i Componenti Fortemente Connessi (SCC)?
UN componente fortemente connessa di un grafo diretto è un sottografo massimale in cui ogni coppia di vertici è reciprocamente raggiungibile. Ciò significa che per due nodi A e B qualsiasi in questo sottografo esiste un percorso da A a B e un percorso da B ad A.
Per esempio: Il grafico seguente ha due componenti fortemente connesse {1,2,3,4} e {5,6,7} poiché esiste un percorso da ciascun vertice a ogni altro vertice nella stessa componente fortemente connessa.

Componente fortemente connesso
Perché i componenti fortemente connessi (SCC) sono importanti?
Comprendere gli SCC è fondamentale per varie applicazioni come:
- Analisi di rete : Identificazione di cluster di nodi strettamente interconnessi.
- Ottimizzazione dei web crawler : Determinazione delle parti del grafico web che sono strettamente collegate.
- Risoluzione delle dipendenze : Nel software, capire quali moduli sono interdipendenti.
Differenza tra componenti connessi e componenti fortemente connessi ( SCC)
Connettività in a grafico non orientato si riferisce al fatto che due vertici siano raggiungibili l'uno dall'altro o meno. Due vertici si dicono connessi se esiste un percorso tra di loro. Nel frattempo Fortemente connesso è applicabile solo a grafici diretti . Un sottografo di un grafo orientato è considerato un Componenti fortemente connessi (SCC) se e solo se per ogni coppia di vertici UN E B , esiste un percorso da UN A B e un percorso da B A UN . Vediamo perché il metodo dfs standard per trovare i componenti collegati in un grafico non può essere utilizzato per determinare componenti fortemente connesse.
Consideriamo il seguente grafico:
Ora iniziamo a dfs chiamata dal vertice 1 per visitare altri vertici.
Perché il metodo DFS convenzionale non può essere utilizzato per trovare le componenti fortemente connesse?
Tutti i vertici possono essere raggiunti dal vertice 1. Ma i vertici 1 e 5,6,7 non possono trovarsi nella stessa componente fortemente connessa perché non esiste un percorso diretto dal vertice 5,6 o 7 al vertice 1. Il grafo ne ha due fortemente componenti connessi {1,2,3,4} e {5,6,7}. Quindi il metodo dfs convenzionale non può essere utilizzato per trovare le componenti fortemente connesse.
polimorfismo
Collegamento di due componenti fortemente connessi tramite un bordo unidirezionale
Due diversi componenti connessi diventano un singolo componente se viene aggiunto uno spigolo tra un vertice di un componente e un vertice di un altro componente. Ma questo non è il caso dei componenti fortemente connessi. Due componenti fortemente connessi non diventano un singolo componente fortemente connesso se c’è solo un bordo unidirezionale da un SCC all’altro SCC.
Banca dati
Approccio della forza bruta per trovare componenti fortemente connessi
Il metodo semplice sarà per ogni vertice i (che non fa parte di alcuna componente forte) trovare i vertici che saranno la parte della componente fortemente connessa contenente il vertice i. Due vertici i e j si troveranno nella stessa componente fortemente connessa se esiste un percorso diretto dal vertice i al vertice j e viceversa.
Comprendiamo l'approccio con l'aiuto del seguente esempio:
- A partire dal vertice 1. C'è un percorso dal vertice 1 al vertice 2 e viceversa. Allo stesso modo esiste un percorso dal vertice 1 al vertice 3 e viceversa. Quindi, i vertici 2 e 3 si troveranno nella stessa componente fortemente connessa del vertice 1. Sebbene esista un percorso diretto dal vertice 1 al vertice 4 e al vertice 5. Ma non esiste un percorso diretto dal vertice 4,5 al vertice 1, quindi il vertice 4 e 5 non saranno nella stessa Componente Fortemente Connessa del vertice 1. Pertanto i Vertici 1,2 e 3 formano una Componente Fortemente Connessa.
- Per i vertici 2 e 3 è già stata determinata la componente fortemente connessa.
- Per il vertice 4, esiste un percorso dal vertice 4 al vertice 5 ma non esiste un percorso dal vertice 5 al vertice 4. Quindi i vertici 4 e 5 non si troveranno nella stessa componente fortemente connessa. Quindi sia il Vertice 4 che il Vertice 5 faranno parte di un unico componente fortemente connesso.
- Quindi ci saranno 3 componenti fortemente connesse {1,2,3}, {4} e {5}.
Di seguito è riportata l'implementazione dell'approccio di cui sopra:
C++ #include using namespace std; class GFG { public: // dfs Function to reach destination bool dfs(int curr, int des, vector>& agg, vettore & vis) { // Se il nodo curr è la destinazione return true if (curr == des) { return true; } vis[curr] = 1; for (auto x : adj[curr]) { if (!vis[x]) { if (dfs(x, des, adj, vis)) { return true; } } } restituisce falso; } // Per sapere se esiste un percorso dall'origine alla // destinazione bool isPath(int src, int des, vettore>& agg) { vettore vis(agg.dimensione() + 1, 0); return dfs(src, des, adj, vis); } // Funzione per restituire tutti i // componenti fortemente connessi di un grafico. vettore> findSCC(int n, vettore>& a) { // Memorizza tutti i componenti fortemente connessi. vettore> ans; // Memorizza se un vertice fa parte di qualsiasi vettore componente fortemente // connesso è_scc(n + 1, 0); vettore> agg(n+1); for (int i = 0; i< a.size(); i++) { adj[a[i][0]].push_back(a[i][1]); } // Traversing all the vertices for (int i = 1; i <= n; i++) { if (!is_scc[i]) { // If a vertex i is not a part of any SCC // insert it into a new SCC list and check // for other vertices whether they can be // thr part of thidl ist. vector scc; scc.push_back(i); for (int j = i + 1; j<= n; j++) { // If there is a path from vertex i to // vertex j and vice versa put vertex j // into the current SCC list. if (!is_scc[j] && isPath(i, j, adj) && isPath(j, i, adj)) { is_scc[j] = 1; scc.push_back(j); } } // Insert the SCC containing vertex i into // the final list. ans.push_back(scc); } } return ans; } }; // Driver Code Starts int main() { GFG obj; int V = 5; vector> bordi{ { 1, 3 }, { 1, 4 }, { 2, 1 }, { 3, 2 }, { 4, 5 } }; vettore> ans = obj.findSCC(V, bordi); cout<< 'Strongly Connected Components are:
'; for (auto x : ans) { for (auto y : x) { cout << y << ' '; } cout << '
'; } }>
Giava import java.util.ArrayList; import java.util.List; class GFG { // dfs Function to reach destination boolean dfs(int curr, int des, List> agg., Lista vis) { // Se il nodo curr è la destinazione return true if (curr == des) { return true; } vis.set(curr, 1); for (int x : adj.get(curr)) { if (vis.get(x) == 0) { if (dfs(x, des, adj, vis)) { return true; } } } restituisce falso; } // Per sapere se esiste un percorso dall'origine alla // destinazione boolean isPath(int src, int des, List> agg) { Lista vis = new ArrayList(adj.size() + 1); for (int i = 0; i<= adj.size(); i++) { vis.add(0); } return dfs(src, des, adj, vis); } // Function to return all the strongly connected // component of a graph. List> findSCC(int n, List> a) { // Memorizza tutti i componenti fortemente connessi. Elenco> ans = new ArrayList(); // Memorizza se un vertice fa parte di un elenco di componenti fortemente // connessi is_scc = nuovo ArrayList(n + 1); for (int i = 0; i<= n; i++) { is_scc.add(0); } List> adj = nuovo ArrayList(); for (int i = 0; i<= n; i++) { adj.add(new ArrayList()); } for (List bordo : a) { adj.get(edge.get(0)).add(edge.get(1)); } // Attraversa tutti i vertici per (int i = 1; i<= n; i++) { if (is_scc.get(i) == 0) { // If a vertex i is not a part of any SCC // insert it into a new SCC list and check // for other vertices whether they can be // the part of this list. List scc = new ArrayList(); scc.add(i); for (int j = i + 1; j<= n; j++) { // If there is a path from vertex i to // vertex j and vice versa, put vertex j // into the current SCC list. if (is_scc.get(j) == 0 && isPath(i, j, adj) && isPath(j, i, adj)) { is_scc.set(j, 1); scc.add(j); } } // Insert the SCC containing vertex i into // the final list. ans.add(scc); } } return ans; } } public class Main { public static void main(String[] args) { GFG obj = new GFG(); int V = 5; List> bordi = new ArrayList(); bordi.add(new ArrayList(List.of(1, 3))); bordi.add(new ArrayList(List.of(1, 4))); bordi.add(new ArrayList(List.of(2, 1))); bordi.add(new ArrayList(List.of(3, 2))); bordi.add(new ArrayList(List.of(4, 5))); Elenco> ans = obj.findSCC(V, bordi); System.out.println('I componenti fortemente connessi sono:'); per (Elenco x : ans) { for (int y : x) { System.out.print(y + ' '); } System.out.println(); } } } // Questo codice è stato fornito da shivamgupta310570>
Pitone class GFG: # dfs Function to reach destination def dfs(self, curr, des, adj, vis): # If current node is the destination, return True if curr == des: return True vis[curr] = 1 for x in adj[curr]: if not vis[x]: if self.dfs(x, des, adj, vis): return True return False # To tell whether there is a path from source to destination def isPath(self, src, des, adj): vis = [0] * (len(adj) + 1) return self.dfs(src, des, adj, vis) # Function to return all the strongly connected components of a graph. def findSCC(self, n, a): # Stores all the strongly connected components. ans = [] # Stores whether a vertex is a part of any Strongly Connected Component is_scc = [0] * (n + 1) adj = [[] for _ in range(n + 1)] for i in range(len(a)): adj[a[i][0]].append(a[i][1]) # Traversing all the vertices for i in range(1, n + 1): if not is_scc[i]: # If a vertex i is not a part of any SCC, insert it into a new SCC list # and check for other vertices whether they can be part of this list. scc = [i] for j in range(i + 1, n + 1): # If there is a path from vertex i to vertex j and vice versa, # put vertex j into the current SCC list. if not is_scc[j] and self.isPath(i, j, adj) and self.isPath(j, i, adj): is_scc[j] = 1 scc.append(j) # Insert the SCC containing vertex i into the final list. ans.append(scc) return ans # Driver Code Starts if __name__ == '__main__': obj = GFG() V = 5 edges = [ [1, 3], [1, 4], [2, 1], [3, 2], [4, 5] ] ans = obj.findSCC(V, edges) print('Strongly Connected Components are:') for x in ans: for y in x: print(y, end=' ') print() # This code is contributed by shivamgupta310570>
C# using System; using System.Collections.Generic; class GFG { // dfs Function to reach destination public bool Dfs(int curr, int des, List> agg., Lista vis) { // Se il nodo curr è la destinazione, restituisce true if (curr == des) { return true; } vis[curr] = 1; foreach (var x in adj[curr]) { if (vis[x] == 0) { if (Dfs(x, des, adj, vis)) { return true; } } } restituisce falso; } // Per sapere se esiste un percorso dall'origine alla destinazione public bool IsPath(int src, int des, List> agg) { var show = nuova lista (Conteggio agg. + 1); for (int i = 0; i< adj.Count + 1; i++) { vis.Add(0); } return Dfs(src, des, adj, vis); } // Function to return all the strongly connected components of a graph public List> FindSCC(int n, List> a) { // Memorizza tutti i componenti fortemente connessi var ans = new List>(); // Memorizza se un vertice fa parte di qualsiasi componente fortemente connesso var isScc = new List (n+1); for (int i = 0; i< n + 1; i++) { isScc.Add(0); } var adj = new List>(n+1); for (int i = 0; i< n + 1; i++) { adj.Add(new List ()); } for (int i = 0; i< a.Count; i++) { adj[a[i][0]].Add(a[i][1]); } // Traversing all the vertices for (int i = 1; i <= n; i++) { if (isScc[i] == 0) { // If a vertex i is not a part of any SCC // insert it into a new SCC list and check // for other vertices whether they can be // the part of this list. var scc = new List (); scc.Add(i); for (int j = i + 1; j<= n; j++) { // If there is a path from vertex i to // vertex j and vice versa, put vertex j // into the current SCC list. if (isScc[j] == 0 && IsPath(i, j, adj) && IsPath(j, i, adj)) { isScc[j] = 1; scc.Add(j); } } // Insert the SCC containing vertex i into // the final list. ans.Add(scc); } } return ans; } } // Driver Code Starts class Program { static void Main(string[] args) { GFG obj = new GFG(); int V = 5; List> bordi = nuova lista> { nuova lista { 1, 3 }, nuova lista { 1, 4 }, nuova lista { 2, 1 }, nuova lista { 3, 2 }, nuova lista { 4, 5 } }; Elenco> ans = obj.FindSCC(V, bordi); Console.WriteLine('I componenti fortemente connessi sono:'); foreach (var x in ans) { foreach (var y in x) { Console.Write(y + ' '); } Console.WriteLine(); } } } // Questo codice è stato fornito da shivamgupta310570>
Javascript class GFG { // Function to reach the destination using DFS dfs(curr, des, adj, vis) { // If the current node is the destination, return true if (curr === des) { return true; } vis[curr] = 1; for (let x of adj[curr]) { if (!vis[x]) { if (this.dfs(x, des, adj, vis)) { return true; } } } return false; } // Check whether there is a path from source to destination isPath(src, des, adj) { const vis = new Array(adj.length + 1).fill(0); return this.dfs(src, des, adj, vis); } // Function to find all strongly connected components of a graph findSCC(n, a) { // Stores all strongly connected components const ans = []; // Stores whether a vertex is part of any Strongly Connected Component const is_scc = new Array(n + 1).fill(0); const adj = new Array(n + 1).fill().map(() =>[]); per (sia i = 0; i< a.length; i++) { adj[a[i][0]].push(a[i][1]); } // Traversing all the vertices for (let i = 1; i <= n; i++) { if (!is_scc[i]) { // If a vertex i is not part of any SCC, // insert it into a new SCC list and check // for other vertices that can be part of this list. const scc = [i]; for (let j = i + 1; j <= n; j++) { // If there is a path from vertex i to // vertex j and vice versa, put vertex j // into the current SCC list. if (!is_scc[j] && this.isPath(i, j, adj) && this.isPath(j, i, adj)) { is_scc[j] = 1; scc.push(j); } } // Insert the SCC containing vertex i into the final list. ans.push(scc); } } return ans; } } // Driver Code Starts const obj = new GFG(); const V = 5; const edges = [ [1, 3], [1, 4], [2, 1], [3, 2], [4, 5] ]; const ans = obj.findSCC(V, edges); console.log('Strongly Connected Components are:'); for (let x of ans) { console.log(x.join(' ')); } // This code is contributed by shivamgupta310570>
Produzione
Strongly Connected Components are: 1 2 3 4 5>
Complessità temporale: O(n * (n + m)), perché per ogni coppia di vertici stiamo controllando se esiste un percorso tra di loro.
Spazio ausiliario: O(N)
siti web di film simili a 123movies
Approccio efficiente per trovare componenti fortemente connessi (SCC)
Per trovare gli SCC in un grafico, possiamo utilizzare algoritmi come Algoritmo di Kosaraju O Algoritmo di Tarjan . Esploriamo questi algoritmi passo dopo passo.
1. Algoritmo di Kosaraju :
L’algoritmo di Kosaraju prevede due fasi principali:
- Esecuzione della ricerca in profondità (DFS) sul grafico originale :
- Per prima cosa eseguiamo un DFS sul grafico originale e registriamo i tempi di fine dei nodi (ovvero il momento in cui il DFS termina di esplorare completamente un nodo).
- Esecuzione di DFS sul grafico trasposto :
- Quindi invertiamo la direzione di tutti gli spigoli nel grafico per creare il grafico trasposto.
- Successivamente, eseguiamo una DFS sul grafo trasposto, considerando i nodi in ordine decrescente in base ai loro tempi finali registrati nella prima fase.
- Ogni attraversamento DFS in questa fase ci darà un SCC.
Ecco una versione semplificata dell’algoritmo di Kosaraju:
- DFS sul grafico originale : Registra i tempi di fine.
- Trasporre il grafico : Inverte tutti i bordi.
- DFS sul grafico trasposto : elabora i nodi in ordine decrescente di tempi di fine per trovare gli SCC.
2. Algoritmo di Tarjan :
L'algoritmo di Tarjan è più efficiente perché trova gli SCC in un singolo passaggio DFS utilizzando uno stack e alcuni calcoli aggiuntivi:
- Attraversamento DFS : Durante il DFS, mantenere un indice per ciascun nodo e l'indice più piccolo (valore di collegamento basso) che può essere raggiunto dal nodo.
- Pila : tiene traccia dei nodi attualmente nello stack di ricorsione (parte dell'SCC corrente in fase di esplorazione).
- Identificazione degli SCC : Quando il valore del collegamento basso di un nodo è uguale al suo indice, significa che abbiamo trovato un SCC. Estrai tutti i nodi dallo stack finché non raggiungiamo il nodo corrente.
Ecco uno schema semplificato dell’algoritmo di Tarjan:
- Inizializzare
index>
a 0. - Per ogni nodo non visitato, esegui DFS.
- Imposta l'indice del nodo e il valore del collegamento basso.
- Spingi il nodo nello stack.
- Per ogni nodo adiacente, esegui DFS se non è visitato o aggiorna il valore del collegamento basso se è nello stack.
- Se il valore del collegamento basso del nodo è uguale al suo indice, estrai i nodi dallo stack per formare un SCC.
Conclusione
Comprendere e trovare componenti fortemente connessi in un grafo diretto è essenziale per molte applicazioni in informatica. Quello di Kosaraju E Quello di Tarjan gli algoritmi sono modi efficienti per identificare gli SCC, ciascuno con il proprio approccio e vantaggi. Padroneggiando questi concetti, puoi analizzare e ottimizzare meglio la struttura e il comportamento di reti complesse.