Skip to content

Commit 6867e83

Browse files
committed
solve(w11): 261. Graph Valid Tree
1 parent 954a865 commit 6867e83

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed

graph-valid-tree/seungriyou.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# https://leetcode.com/problems/graph-valid-tree/
2+
3+
from typing import List
4+
5+
class Solution:
6+
def validTree_bfs(self, n: int, edges: List[List[int]]) -> bool:
7+
"""
8+
[Complexity] (첫 번째 조건을 통과하면 e = n - 1일 것이므로, n으로 변환도 가능)
9+
- TC: O(v + e) (== O(n + len(edges)))
10+
- SC: O(v + e) (graph)
11+
12+
[Approach]
13+
valid tree란 다음의 두 조건을 만족하는 undirected graph이다.
14+
1) acyclic -> edges의 개수가 n - 1개인지 확인 (-> early stop 가능)
15+
2) connected -> 모든 node가 모두 방문되었는지, 즉 len(visited) == n인지 확인
16+
17+
이러한 조건을 BFS로 확인할 수 있다.
18+
"""
19+
from collections import deque
20+
21+
# edge의 개수가 n - 1이 아니라면 빠르게 반환 (-> acyclic & connected 여부 확인)
22+
if len(edges) != n - 1:
23+
return False
24+
25+
# undirected graph 구성
26+
graph = [[] for _ in range(n)]
27+
for a, b in edges:
28+
graph[a].append(b)
29+
graph[b].append(a)
30+
31+
# 0번 노드부터 시작
32+
visited = {0}
33+
q = deque([0])
34+
35+
# BFS
36+
while q:
37+
pos = q.popleft()
38+
for npos in graph[pos]:
39+
if npos not in visited:
40+
visited.add(npos)
41+
q.append(npos)
42+
43+
# visited에 모든 노드가 들어가있다면 true (-> connected 여부 확인)
44+
return len(visited) == n
45+
46+
def validTree_dfs(self, n: int, edges: List[List[int]]) -> bool:
47+
"""
48+
[Complexity]
49+
- TC: O(v + e) (== O(n + len(edges)))
50+
- SC: O(v + e) (graph) (call stack의 경우 O(v))
51+
52+
[Approach]
53+
valid tree의 조건을 DFS로 확인할 수 있다.
54+
"""
55+
# edge의 개수가 n - 1이 아니라면 빠르게 반환 (-> acyclic & connected 여부 확인)
56+
if len(edges) != n - 1:
57+
return False
58+
59+
# undirected graph 구성
60+
graph = [[] for _ in range(n)]
61+
for a, b in edges:
62+
graph[a].append(b)
63+
graph[b].append(a)
64+
65+
visited = set()
66+
67+
def dfs(pos):
68+
# base condition
69+
if pos in visited:
70+
return
71+
72+
# visit 처리
73+
visited.add(pos)
74+
75+
# recur
76+
for npos in graph[pos]:
77+
dfs(npos)
78+
79+
dfs(0)
80+
81+
# visited에 모든 노드가 들어가있다면 true (-> connected 여부 확인)
82+
return len(visited) == n
83+
84+
def validTree(self, n: int, edges: List[List[int]]) -> bool:
85+
"""
86+
[Complexity]
87+
- TC: O(e * α(v))
88+
- e 만큼 반복
89+
- 각 union-find는 path compression으로 인해 α(v)이며, 이는 거의 상수
90+
=> e = n - 1이므로 O(n)으로도 표현 가능
91+
- SC: O(n)
92+
93+
[Approach]
94+
union-find로 undirected graph의 cycle 여부를 판단할 수 있다.
95+
따라서 valid tree의 두 조건 중 connected 조건을 확인할 때 union-find를 사용할 수 있다.
96+
1) acyclic -> edges의 개수가 n - 1개인지 확인 (-> early stop 가능)
97+
2) connected -> union-find 시 parent가 같은 경우가 있는지 확인
98+
"""
99+
# edge의 개수가 n - 1이 아니라면 빠르게 반환 (-> acyclic & connected 여부 확인)
100+
if len(edges) != n - 1:
101+
return False
102+
103+
# union-find functions
104+
def find_parent(x):
105+
if parent[x] != x:
106+
parent[x] = find_parent(parent[x])
107+
return parent[x]
108+
109+
def union_parent(x, y):
110+
px, py = find_parent(x), find_parent(y)
111+
112+
# cyclic 하다면(= x와 y의 parent가 같다면) False 반환
113+
if px == py:
114+
return False
115+
116+
# union
117+
if px < py:
118+
parent[py] = px
119+
else:
120+
parent[px] = py
121+
122+
return True
123+
124+
# perform union-find
125+
parent = [i for i in range(n)]
126+
for x, y in edges:
127+
if not union_parent(x, y):
128+
return False
129+
130+
return True

0 commit comments

Comments
 (0)