logo

Componenti fortemente connessi

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)?

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.

scc_fianldrawio

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:



scc_fianldrawio

Ora iniziamo a dfs chiamata dal vertice 1 per visitare altri vertici.

dfs_finaldrawio

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

unidrawio-(2)

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:

disegno di 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:

  1. 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).
  2. 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:

  1. DFS sul grafico originale : Registra i tempi di fine.
  2. Trasporre il grafico : Inverte tutti i bordi.
  3. 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:

  1. 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.
  2. Pila : tiene traccia dei nodi attualmente nello stack di ricorsione (parte dell'SCC corrente in fase di esplorazione).
  3. 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:

  1. Inizializzareindex>a 0.
  2. 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.