|
| 1 | +''' |
| 2 | +# 323. Number of Connected Components in an Undirected Graph |
| 3 | +
|
| 4 | +- 무방향 그래프에서 연결된 컴포넌트(connected component)의 개수 구하기 |
| 5 | +- 노드 개수 n: 0 ~ n-1 |
| 6 | +- 간선 리스트 edges: 노드 쌍 [u, v] |
| 7 | +- 연결된 컴포넌트의 개수를 반환한다. |
| 8 | +
|
| 9 | +## 그래프 |
| 10 | +- edges를 통해 인접 리스트(adjacency list)를 생성한다. |
| 11 | +- 무방향 그래프이므로, u ↔ v 양쪽 방향으로 연결 |
| 12 | +
|
| 13 | +## 풀이 알고리즘 |
| 14 | +- DFS(스택)/BFS(큐) |
| 15 | + - 각 컴포넌트를 탐색하며 방문 처리 |
| 16 | + - 방문하지 않은 노드를 찾으면 새로운 컴포넌트이므로 카운트 증가 |
| 17 | +- Union-Find |
| 18 | + - 각 노드가 어떤 그룹에 속하는 지 parent 배열로 표현 |
| 19 | + - find로 부모를 찾고, union으로 노드를 합친다. |
| 20 | + - 최종적으로 독립된 그룹의 개수를 반환한다. |
| 21 | +''' |
| 22 | + |
| 23 | +''' |
| 24 | +1. DFS(스택) |
| 25 | +''' |
| 26 | +def countComponentsDFS(self, n: int, edges: List[List[int]]) -> int: |
| 27 | + graph = defaultdict(list) |
| 28 | + for u, v in edges: # 인접 리스트 생성 |
| 29 | + graph[u].append(v) |
| 30 | + graph[v].append(u) |
| 31 | + |
| 32 | + visited = set() # 방문처리할 set |
| 33 | + count = 0 |
| 34 | + |
| 35 | + def dfs(start): |
| 36 | + stack = [start] |
| 37 | + visited.add(start) |
| 38 | + |
| 39 | + while stack: |
| 40 | + curr = stack.pop() |
| 41 | + for neighbor in graph[curr]: |
| 42 | + if neighbor not in visited: |
| 43 | + visited.add(neighbor) |
| 44 | + stack.append(neighbor) |
| 45 | + |
| 46 | + for node in range(n): |
| 47 | + if node not in visited: |
| 48 | + dfs(node) |
| 49 | + count += 1 |
| 50 | + |
| 51 | + return count |
| 52 | + |
| 53 | +''' |
| 54 | +2. BFS(큐) |
| 55 | +''' |
| 56 | +def countComponentsBFS(self, n: int, edges: List[List[int]]) -> int: |
| 57 | + graph = defaultdict(list) |
| 58 | + for u, v in edges: # 인접 리스트 생성 |
| 59 | + graph[u].append(v) |
| 60 | + graph[v].append(u) |
| 61 | + |
| 62 | + visited = set() # 방문처리할 set |
| 63 | + count = 0 |
| 64 | + |
| 65 | + def bfs(start): |
| 66 | + queue = deque([start]) |
| 67 | + visited.add(start) |
| 68 | + |
| 69 | + while queue: |
| 70 | + curr = queue.popleft() |
| 71 | + for neighbor in graph[curr]: |
| 72 | + if neighbor not in visited: |
| 73 | + visited.add(neighbor) |
| 74 | + queue.append(neighbor) |
| 75 | + |
| 76 | + for node in range(n): |
| 77 | + if node not in visited: |
| 78 | + bfs(node) |
| 79 | + count += 1 |
| 80 | + |
| 81 | + return count |
| 82 | + |
| 83 | +''' |
| 84 | +3. Union-Find |
| 85 | +- 간선을 순회하며 두 노드를 union으로 합친다.(parent 업데이트) |
| 86 | +- find는 경로 압축을 통해 대표(root)를 찾는다.(parent에서 재귀로 찾기) |
| 87 | +- 최종 root를 조사하고, 서로 다른 root의 개수를 반환한다. |
| 88 | +''' |
| 89 | +def countComponentsUnionFind(self, n: int, edges: List[List[int]]) -> int: |
| 90 | + parent = list(range(n)) # 각 노드의 부모를 자신으로 초기화 |
| 91 | + |
| 92 | + def find(x): |
| 93 | + if parent[x] != x: |
| 94 | + parent[x] = find(parent[x]) # 경로 압축 path compression |
| 95 | + return parent[x] |
| 96 | + |
| 97 | + def union(x, y): |
| 98 | + rootX = find(x) |
| 99 | + rootY = find(y) |
| 100 | + if rootX != rootY: |
| 101 | + parent[rootX] = rootY |
| 102 | + |
| 103 | + for u, v in edges: |
| 104 | + union(u, v) |
| 105 | + |
| 106 | + # 모든 노드의 최종 부모의, 유일한 root의 개수를 반환 |
| 107 | + return len(set(find(i) for i in range(n))) |
0 commit comments