logo

강하게 연결된 구성 요소

강하게 연결된 구성요소(SCC) 그래프 이론과 알고리즘의 기본 개념입니다. 방향성 그래프에서, 강하게 연결된 구성요소 하위 집합의 모든 정점은 방향이 있는 가장자리를 통과하여 동일한 하위 집합의 다른 모든 정점에서 도달할 수 있는 정점의 하위 집합입니다. 찾기 SCC 그래프의 구조와 연결성에 대한 중요한 통찰력을 제공할 수 있으며, 다음과 같은 다양한 분야에 응용할 수 있습니다. 소셜 네트워크 분석, 웹 크롤링, 네트워크 라우팅 . 이 튜토리얼에서는 그래프 데이터 구조에서 강력하게 연결된 구성요소를 식별하기 위한 정의, 속성 및 효율적인 알고리즘을 살펴봅니다.

자바 사용자 입력

내용의 테이블



SCC(강하게 연결된 구성요소)란 무엇입니까?

강하게 연결된 구성 요소 방향 그래프의 모든 정점 쌍이 상호 도달 가능한 최대 하위 그래프입니다. 이는 이 하위 그래프의 임의의 두 노드 A와 B에 대해 A에서 B로의 경로와 B에서 A로의 경로가 있음을 의미합니다.

예를 들어: 아래 그래프에는 두 개의 강력하게 연결된 구성 요소 {1,2,3,4} 및 {5,6,7}가 있습니다. 동일한 강력하게 연결된 구성 요소의 각 꼭지점에서 다른 모든 꼭지점까지의 경로가 있기 때문입니다.

scc_fianldrawio

강하게 연결된 구성요소



SCC(강하게 연결된 구성 요소)가 중요한 이유는 무엇입니까?

SCC를 이해하는 것은 다음과 같은 다양한 애플리케이션에 중요합니다.

  • 네트워크 분석 : 밀접하게 상호 연결된 노드의 클러스터를 식별합니다.
  • 웹 크롤러 최적화 : 웹 그래프에서 밀접하게 연결된 부분을 결정합니다.
  • 종속성 해결 : 소프트웨어에서 어떤 모듈이 상호의존적인지 이해합니다.

연결된 구성요소와 강하게 연결된 구성요소의 차이점( SCC)

연결성 무방향 그래프 두 정점이 서로 도달할 수 있는지 여부를 나타냅니다. 두 정점 사이에 경로가 있으면 두 정점이 연결되어 있다고 합니다. 그 동안에 강하게 연결됨 에만 적용됩니다 방향성 그래프 . 유향 그래프의 하위 그래프는 다음과 같은 것으로 간주됩니다. 강하게 연결된 구성 요소 (SCC) 모든 정점 쌍에 대해 if 및 only if 그리고 , 다음 경로가 존재합니다. 에게 그리고 경로 에게 . 왜 그런지 살펴보겠습니다. 연결된 구성 요소를 찾는 표준 dfs 방법 그래프에서는 강하게 연결된 구성 요소를 결정하는 데 사용할 수 없습니다.

다음 그래프를 고려하십시오.



scc_fianldrawio

이제 시작해보자 dfs 정점 1에서 호출하여 다른 정점을 방문합니다.

dfs_finaldrawio

기존 DFS 방법을 사용하여 강하게 연결된 구성 요소를 찾을 수 없는 이유는 무엇입니까?

모든 정점은 정점 1에서 도달할 수 있습니다. 그러나 정점 5,6 또는 7에서 정점 1로 향하는 경로가 없기 때문에 정점 1과 정점 5,6,7은 동일한 강력하게 연결된 구성요소에 있을 수 없습니다. 연결된 구성요소 {1,2,3,4} 및 {5,6,7}. 따라서 기존의 dfs 방법은 강하게 연결된 구성 요소를 찾는 데 사용할 수 없습니다.

자바에서 엑셀 파일 읽기

단방향 에지로 강력하게 연결된 두 구성 요소 연결

두 개의 서로 다른 연결된 구성 요소는 한 구성 요소의 꼭지점과 다른 구성 요소의 꼭지점 사이에 모서리가 추가되면 단일 구성 요소가 됩니다. 그러나 강하게 연결된 구성 요소에서는 그렇지 않습니다. 한 SCC에서 다른 SCC로의 단방향 에지만 있는 경우 두 개의 강하게 연결된 구성 요소는 하나의 강하게 연결된 구성 요소가 되지 않습니다.

난수 C 코드

유니드라위오-(2)

강하게 연결된 구성 요소를 찾기 위한 무차별 접근 방식

간단한 방법은 각 정점 i(강력한 구성 요소의 일부가 아님)에 대해 정점 i를 포함하는 강하게 연결된 구성 요소의 일부가 될 정점을 찾는 것입니다. 정점 i에서 정점 j로 또는 그 반대로 방향이 지정된 경로가 있는 경우 두 정점 i와 j는 동일한 강력하게 연결된 구성 요소에 속하게 됩니다.

다음 예를 통해 접근 방식을 이해해 보겠습니다.

예시 무승부

  • 꼭지점 1부터 시작합니다. 꼭지점 1에서 꼭지점 2로 또는 그 반대의 경로가 있습니다. 마찬가지로 정점 1에서 정점 3으로 또는 그 반대의 경로가 있습니다. 따라서 정점 2와 3은 정점 1과 동일한 Strongly Connected Component에 있을 것입니다. 비록 정점 1에서 정점 4와 정점 5로 향하는 경로가 있지만 정점 4,5에서 정점 1로 향하는 경로는 없으므로 정점 4는 없습니다. 5는 정점 1과 동일한 Strongly Connected Component에 있지 않습니다. 따라서 Vertex 1,2, 3은 Strongly Connected Component를 형성합니다.
  • Vertex 2와 3의 경우 Strongly Connected Component가 이미 결정되어 있습니다.
  • 정점 4의 경우 정점 4에서 정점 5로의 경로가 있지만 정점 5에서 정점 4로의 경로는 없습니다. 따라서 정점 4와 5는 동일한 Strongly Connected Component에 속하지 않습니다. 따라서 Vertex 4와 Vertex 5는 모두 Single Strongly Connected Component의 일부가 됩니다.
  • 따라서 3개의 강력하게 연결된 구성요소 {1,2,3}, {4} 및 {5}가 있습니다.

다음은 위의 접근 방식을 구현한 것입니다.

C++
#include  using namespace std; class GFG { public:  // dfs Function to reach destination  bool dfs(int curr, int des, vector>& 조정, 벡터 & vis) { // curr 노드가 대상인 경우 true를 반환합니다. if (curr == des) { return true;  } 비스[현재] = 1;  for (auto x : adj[curr]) { if (!vis[x]) { if (dfs(x, des, adj, vis)) { return true;  } } } false를 반환합니다.  } // 소스에서 대상까지 // 경로가 있는지 여부를 알려줍니다. bool isPath(int src, int des, vector>& 조정) { 벡터 vis(adj.size() + 1, 0);  return dfs(src, des, adj, vis);  } // 강하게 연결된 그래프의 모든 구성 요소를 // 반환하는 함수입니다.  벡터> findSCC(int n, 벡터>& a) { // 강하게 연결된 모든 구성 요소를 저장합니다.  벡터> 답변;  // 정점이 강력하게 연결된 구성 요소 벡터의 일부인지 여부를 저장합니다. is_scc(n + 1, 0);  벡터> 조정(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> 가장자리{ { 1, 3 }, { 1, 4 }, { 2, 1 }, { 3, 2 }, { 4, 5 } };  벡터> ans = obj.findSCC(V, 가장자리);  시합<< 'Strongly Connected Components are:
';  for (auto x : ans) {  for (auto y : x) {  cout << y << ' ';  }  cout << '
';  } }>
자바
import java.util.ArrayList; import java.util.List; class GFG {  // dfs Function to reach destination  boolean dfs(int curr, int des, List> 조정, 목록 vis) { // curr 노드가 대상인 경우 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;  } } } false를 반환합니다.  } // 소스에서 대상까지의 경로가 있는지 // 알려줍니다. boolean isPath(int src, int des, List> 조정) { 목록 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, 목록> a) { // 강하게 연결된 모든 구성 요소를 저장합니다.  목록> ans = 새로운 ArrayList();  // 정점이 강력하게 연결된 구성 요소 목록의 일부인지 여부를 저장합니다. is_scc = new ArrayList(n + 1);  for (int i = 0; i<= n; i++) {  is_scc.add(0);  }  List> 조정 = 새로운 ArrayList();  for (int i = 0; i<= n; i++) {  adj.add(new ArrayList());  }  for (List 가장자리 : a) { adj.get(edge.get(0)).add(edge.get(1));  } // 모든 정점을 순회 for (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 = 새로운 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> 가장자리 = 새로운 ArrayList();  edge.add(new ArrayList(List.of(1, 3)));  edge.add(new ArrayList(List.of(1, 4)));  edge.add(new ArrayList(List.of(2, 1)));  edge.add(new ArrayList(List.of(3, 2)));  edge.add(new ArrayList(List.of(4, 5)));  목록> ans = obj.findSCC(V, 가장자리);  System.out.println('강하게 연결된 구성 요소는 다음과 같습니다.');  (목록 x : ans) { for (int y : x) { System.out.print(y + ' ');  } System.out.println();  } } } // 이 코드는 shivamgupta310570이 제공한 것입니다.>
파이썬
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>
씨#
using System; using System.Collections.Generic; class GFG {  // dfs Function to reach destination  public bool Dfs(int curr, int des, List> 조정, 목록 vis) { // curr 노드가 목적지인 경우 true를 반환합니다. if (curr == des) { return true;  } 비스[현재] = 1;  foreach (var x in adj[curr]) { if (vis[x] == 0) { if (Dfs(x, des, adj, vis)) { return true;  } } } false를 반환합니다.  } // 소스에서 대상까지의 경로가 있는지 확인하기 위해 public bool IsPath(int src, int des, List> adj) { var show = 새 목록 (adj.개수 + 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, 목록> a) { // 강하게 연결된 모든 구성요소를 저장합니다. var ans = new List>();  // 정점이 Strongly Connected Component의 일부인지 여부를 저장합니다. 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> 가장자리 = 새 목록> { 새 목록 { 1, 3 }, 새 목록 { 1, 4 }, 새 목록 { 2, 1 }, 새 목록 { 3, 2 }, 새 목록 { 4, 5 } };  목록> ans = obj.FindSCC(V, 가장자리);  Console.WriteLine('강하게 연결된 구성 요소는 다음과 같습니다.');  foreach(var x in ans) { foreach(var y in x) { Console.Write(y + ' ');  } Console.WriteLine();  } } } // 이 코드는 shivamgupta310570이 제공한 것입니다.>
자바스크립트
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(() =>[]);  for (나는 = 0이라고 하자; 나는< 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>

산출
Strongly Connected Components are: 1 2 3 4 5>

시간 복잡도: O(n * (n + m)), 각 정점 쌍에 대해 정점 사이에 경로가 존재하는지 확인하기 때문입니다.
보조공간 : O(N)

채팅할 문자열

강하게 연결된 구성요소(SCC)를 찾는 효율적인 접근 방식

그래프에서 SCC를 찾으려면 다음과 같은 알고리즘을 사용할 수 있습니다. 코사라주의 알고리즘 또는 타잔의 알고리즘 . 이러한 알고리즘을 단계별로 살펴보겠습니다.

1. 코사라주의 알고리즘 :

Kosaraju의 알고리즘은 두 가지 주요 단계로 구성됩니다.

  1. 원본 그래프에서 깊이 우선 검색(DFS) 수행 :
    • 먼저 원본 그래프에서 DFS를 수행하고 노드의 완료 시간(즉, DFS가 노드 탐색을 완전히 완료하는 시간)을 기록합니다.
  2. 전치된 그래프에서 DFS 수행 :
    • 그런 다음 그래프의 모든 간선 방향을 바꾸어 전치된 그래프를 만듭니다.
    • 다음으로, 첫 번째 단계에서 기록된 완료 시간의 내림차순으로 노드를 고려하여 전치된 그래프에서 DFS를 수행합니다.
    • 이 단계의 각 DFS 탐색은 하나의 SCC를 제공합니다.

다음은 Kosaraju 알고리즘의 단순화된 버전입니다.

  1. 원본 그래프의 DFS : 완료 시간을 기록합니다.
  2. 그래프 전치 : 모든 가장자리를 반전시킵니다.
  3. 전치 그래프의 DFS : 완료 시간이 감소하는 순서대로 노드를 처리하여 SCC를 찾습니다.

2. 타잔의 알고리즘 :

Tarjan의 알고리즘은 스택과 일부 추가 장부를 사용하여 단일 DFS 패스에서 SCC를 찾기 때문에 더 효율적입니다.

  1. DFS 순회 : DFS 동안 각 노드에 대한 인덱스와 그 노드에서 도달할 수 있는 가장 작은 인덱스(로우링크 값)를 유지한다.
  2. 스택 : 현재 재귀 스택(탐색 중인 현재 SCC의 일부)에 있는 노드를 추적합니다.
  3. SCC 식별 : 노드의 낮은 링크 값이 해당 인덱스와 같으면 SCC를 찾았다는 의미입니다. 현재 노드에 도달할 때까지 스택에서 모든 노드를 팝합니다.

Tarjan 알고리즘의 단순화된 개요는 다음과 같습니다.

  1. 초기화index>0으로.
  2. 방문하지 않은 각 노드에 대해 DFS를 수행합니다.
    • 노드의 인덱스와 낮은 링크 값을 설정합니다.
    • 노드를 스택에 푸시합니다.
    • 각 인접 노드에 대해 방문하지 않은 경우 DFS를 수행하거나 스택에 있는 경우 낮은 링크 값을 업데이트합니다.
    • 노드의 낮은 링크 값이 해당 인덱스와 같으면 스택에서 노드를 팝하여 SCC를 형성합니다.

결론

이해와 발견 강하게 연결된 구성요소 유향 그래프는 컴퓨터 과학의 많은 응용 분야에 필수적입니다. 코사라쥬의 그리고 타잔의 알고리즘은 각각 고유한 접근 방식과 장점이 있는 SCC를 식별하는 효율적인 방법입니다. 이러한 개념을 익히면 복잡한 네트워크의 구조와 동작을 더 잘 분석하고 최적화할 수 있습니다.