Skip to content

Commit 869e90b

Browse files
committed
add solution: course-schedule
1 parent a78e183 commit 869e90b

File tree

1 file changed

+184
-0
lines changed

1 file changed

+184
-0
lines changed

โ€Žcourse-schedule/dusunax.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
'''
2+
# 207
3+
์ฐธ๊ณ  ์˜์ƒ: https://www.youtube.com/watch?v=EgI5nU9etnU
4+
๋ฌธ์ œ ํ’€์ด: https://www.algodale.com/problems/course-schedule/
5+
6+
## ๋ฌธ์ œ ์ •๋ฆฌ
7+
๐Ÿ‘‰ prerequisites๋ž€? ํ•„์ˆ˜ ์„ ์ˆ˜ ๊ณผ๋ชฉ์ด๋‹ค.
8+
๐Ÿ‘‰ ๋ฐฉํ–ฅ์„ฑ์ด ์žˆ๋Š” ์—ฐ๊ฒฐ ๊ด€๊ณ„์ด๋ฏ€๋กœ, Directed Graph๋‹ค.
9+
๐Ÿ‘‰ Cycle ๋ฐœ์ƒ ์‹œ, ์ฝ”์Šค๋ฅผ ์ด์ˆ˜ํ•  ์ˆ˜ ์—†๋‹ค.(์„œ๋กœ ์˜์กดํ•˜๋Š” ์ˆœํ™˜์ด ์žˆ์–ด์„œ ๋์—†์ด ๋Œ๊ฒŒ ๋˜๋Š” ๊ฒฝ์šฐ)
10+
11+
## ํ•ด๊ฒฐ ๋ฐฉ์‹ ๋‘๊ฐ€์ง€
12+
1. BFS, Queue, Topological Sort: ์œ„์ƒ ์ •๋ ฌ
13+
2. DFS, Cycle Detection: ์ˆœํ™˜ ํƒ์ง€
14+
15+
### ์œ„์ƒ ์ •๋ ฌ(Topological Sort) - BFS, Queue
16+
- ์ง„์ž… ์ฐจ์ˆ˜(indegree): ๋…ธ๋“œ๋กœ ๋“ค์–ด์˜ค๋Š” ํ™”์‚ดํ‘œ ์ˆ˜
17+
- ๊ทธ๋ž˜ํ”„๋กœ ์ธ์ ‘ ๋ฆฌ์ŠคํŠธ ๊ตฌ์„ฑ
18+
- Queue์— ๋„ฃ๊ธฐ
19+
- Queue BFS ํƒ์ƒ‰
20+
- ๋ชจ๋“  ๊ณผ๋ชฉ์„ ๋“ค์—ˆ๋Š”์ง€ ํ™•์ธ
21+
22+
### ์ˆœํ™˜ ํƒ์ง€(Cycle Detection) - DFS
23+
- ๊ทธ๋ž˜ํ”„๋กœ ์ธ์ ‘ ๋ฆฌ์ŠคํŠธ ๊ตฌ์„ฑ
24+
- ๋ฐฉ๋ฌธ ์ƒํƒœ ๋ฐฐ์—ด ์ดˆ๊ธฐํ™”
25+
- dfs ํ•จ์ˆ˜
26+
- ๋ชจ๋“  ๋…ธ๋“œ์— ๋Œ€ํ•ด dfs ์‹คํ–‰
27+
28+
## TC & SC
29+
- ์‹œ๊ฐ„ ๋ณต์žก๋„์™€ ๊ณต๊ฐ„ ๋ณต์žก๋„๋Š” O(V + E)๋กœ ๋™์ผํ•˜๋‹ค.
30+
```
31+
V: ๋…ธ๋“œ ์ˆ˜(๊ณผ๋ชฉ ์ˆ˜)
32+
E: ๊ฐ„์„  ์ˆ˜(์„ ์ˆ˜ ๊ณผ๋ชฉ ๊ด€๊ณ„ ์ˆ˜)
33+
```
34+
35+
### TC is O(V + E)
36+
37+
๋‘ ๋ฐฉ๋ฒ• ๋ชจ๋‘, ๊ทธ๋ž˜ํ”„์˜ ๋ชจ๋“  ๋…ธ๋“œ์™€ ๊ฐ„์„ ์„ ํ•œ ๋ฒˆ์”ฉ ํ™•์ธํ•จ
38+
- BFS: ๋ชจ๋“  V๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ, ๊ฐ ๋…ธ๋“œ์—์„œ ๋‚˜๊ฐ€๋Š” E๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ์ฐจ์ˆ˜๋ฅผ ์ค„์ž„
39+
- DFS: ๋ชจ๋“  V๋ฅผ ์ˆœํšŒํ•˜๋ฉด์„œ, ๊ฐ ๋…ธ๋“œ์—์„œ ์—ฐ๊ฒฐ๋œ E๋ฅผ ๋”ฐ๋ผ๊ฐ€๋ฉฐ ๊นŠ์ด ํƒ์ƒ‰
40+
41+
### SC is O(V + E)
42+
- O(V+E): V + E๋ฅผ ์ €์žฅํ•˜๋Š” ์ธ์ ‘ ๋ฆฌ์ŠคํŠธ ๊ทธ๋ž˜ํ”„
43+
- O(V)'s: ๋ฐฉ๋ฌธ ์ƒํƒœ ์ €์žฅ, ์ง„์ž… ์ฐจ์ˆ˜ ๋ฐฐ์—ด, BFS ํ, DFS ํ˜ธ์ถœ ์Šคํƒ
44+
45+
## ์œ„์ƒ์ •๋ ฌ(BFS) vs ์ˆœํ™˜ํƒ์ง€(DFS)๐Ÿค”
46+
47+
### BFS๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ
48+
- ๋ฐ˜๋ณต๋ฌธ์„ ์‚ฌ์šฉํ•œ BFS๊ฐ€ indegree(์ง„์ž…์ฐจ์ˆ˜) ๊ฐœ๋…์ด ๋ณด๋‹ค ์ง๊ด€์ ์ด๋ฏ€๋กœ => "์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€ ํ™•์ธ"ํ•  ๋•Œ ๋ช…ํ™•ํ•˜๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ง„์ž… ์ฐจ์ˆ˜๊ฐ€ 0์ธ ๋…ธ๋“œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์„œ ์ฒ˜๋ฆฌ
49+
- ์„ ์ˆ˜ ๊ณผ๋ชฉ์„ ๋‹ค ๋“ค์€ ๊ณผ๋ชฉ์€ ์ง„์ž… ์ฐจ์ˆ˜๊ฐ€ 0์ด ๋˜๋ฏ€๋กœ ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋Š” ๊ณผ๋ชฉ์ด๋ผ๋Š” ์ ์ด ๋ช…ํ™•ํ•จ
50+
```
51+
ํ‚ค์›Œ๋“œ: ์ฒ˜๋ฆฌ ์ˆœ์„œ๋ฅผ ์ถœ๋ ฅ, ์ˆœ์„œ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€
52+
```
53+
54+
### DFS๋ฅผ ์‚ฌ์šฉํ–ˆ์„ ๋•Œ
55+
- DFS ์ˆœํ™˜ ํƒ์ง€๋Š” "์ˆœํ™˜ ์—ฌ๋ถ€"๊ฐ€ ํ•ต์‹ฌ์ผ ๋•Œ ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.
56+
- ์ƒํƒœ(Status)๋ฅผ ์‚ฌ์šฉํ•ด์„œ, ๋ฐฉ๋ฌธ ์ค‘์ธ ๋…ธ๋“œ ์ƒํƒœ๋ฅผ ๋‹ค์‹œ ๋ฐฉ๋ฌธํ•œ๋‹ค๋ฉด ์ˆœํ™˜์ด ์žˆ์Œ์„ ๋ฐ”๋กœ ์•Œ ์ˆ˜ ์žˆ๋‹ค.
57+
- ์ˆœํ™˜์ด ๋ฐœ๊ฒฌ๋˜๋ฉด ๋ฐ”๋กœ ์ค‘๋‹จํ•˜๋ฏ€๋กœ, ์ˆœํ™˜ ํƒ์ง€์— ์ž์—ฐ์Šค๋Ÿฝ๋‹ค.
58+
```
59+
ํ‚ค์›Œ๋“œ: ์‚ฌ์ดํด์ด ์žˆ๋Š”์ง€ ํŒ๋‹จ
60+
```
61+
62+
### +a) `@cache`๋ฅผ ํ™œ์šฉํ•ด๋ณด์ž.
63+
- ํŒŒ์ด์„  3.9~ ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ํ•จ์ˆ˜
64+
- ์ˆœ์ˆ˜ ํ•จ์ˆ˜ + ์žฌ๊ท€ ์ตœ์ ํ™”์— ์‚ฌ์šฉ (์™ธ๋ถ€ ์˜์กด์„ฑ, ๋ถ€์ˆ˜ํšจ๊ณผ์— ์ฃผ์˜ํ•  ๊ฒƒ)
65+
'''
66+
from enum import Enum
67+
68+
class Status(Enum): # use it to dfs
69+
INITIAL = 1
70+
IN_PROGRESS = 2
71+
FINISHED = 3
72+
73+
class Solution:
74+
'''
75+
1. BFS
76+
์œ„์ƒ ์ •๋ ฌ
77+
'''
78+
def canFinishTopologicalSort(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
79+
indegree = [0] * numCourses
80+
graph = defaultdict(list)
81+
82+
for dest, src in prerequisites:
83+
graph[src].append(dest)
84+
indegree[dest] += 1
85+
86+
queue = deque([i for i in range(numCourses) if indegree[i] == 0])
87+
processed_count = 0
88+
89+
while queue:
90+
node = queue.popleft()
91+
processed_count += 1
92+
for neighbor in graph[node]:
93+
indegree[neighbor] -= 1
94+
if indegree[neighbor] == 0:
95+
queue.append(neighbor)
96+
97+
return processed_count == numCourses
98+
99+
'''
100+
2. DFS
101+
์ˆœํ™˜ ํƒ์ง€
102+
'''
103+
def canFinishCycleDetection(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
104+
graph = defaultdict(list)
105+
106+
for dest, src in prerequisites:
107+
graph[src].append(dest)
108+
109+
statuses = {i: Status.INITIAL for i in range(numCourses)}
110+
111+
def dfs(node):
112+
if statuses[node] == Status.IN_PROGRESS:
113+
return False
114+
if statuses[node] == Status.FINISHED:
115+
return True
116+
117+
statuses[node] = Status.IN_PROGRESS
118+
for neighbor in graph[node]:
119+
if not dfs(neighbor):
120+
return False
121+
statuses[node] = Status.FINISHED
122+
return True
123+
124+
return all(dfs(crs) for crs in range(numCourses))
125+
126+
'''
127+
3. @cache
128+
129+
ํŒŒ์ด์ฌ 3.9 ์ด์ƒ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜ ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ
130+
- ๋™์ผ ์ž…๋ ฅ -> ๋™์ผ ์ถœ๋ ฅ์„ ๋ณด์žฅํ•œ๋‹ค.
131+
- 128๊ฐœ ๊นŒ์ง€๋งŒ ์ €์žฅํ•˜๋Š” @lru_cache๋„ ์žˆ๋‹ค.
132+
'''
133+
def canFinishWithCache(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
134+
graph = defaultdict(list)
135+
136+
for dest, src in prerequisites:
137+
graph[src].append(dest)
138+
139+
traversing = set()
140+
141+
@cache
142+
def dfs(node):
143+
if node in traversing:
144+
return False
145+
146+
traversing.add(node)
147+
result = all(dfs(pre) for pre in graph[node])
148+
traversing.remove(node)
149+
return result
150+
151+
return all(dfs(node) for node in range(numCourses))
152+
153+
'''
154+
4. visited๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ
155+
156+
@cache ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ๋Š” ๋ฉ”๋ชจ์ด์ œ์ด์…˜, ๊ฐ™์€ ์ž…๋ ฅ๊ฐ’์— ๋”ฐ๋ผ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ฒŒํ•จ
157+
๊ฒฐ๊ณผ๊ฐ€ ๋ณ€ํ•˜์ง€ ์•Š์„ ๋•Œ ์œ ์šฉํ•จ => dfs(node)๋Š” ์™ธ๋ถ€ ์ƒํƒœ ์ˆœํ™˜ traversing์— ์˜์กดํ•ด์„œ ๋™์ž‘์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค.
158+
๋”ฐ๋ผ์„œ visited set์ด ๋” ์ž์—ฐ์Šค๋Ÿฌ์šธ ์ˆ˜ ์žˆ๋‹ค
159+
'''
160+
def canFinishWithVisited(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
161+
graph = defaultdict(list)
162+
163+
for dest, src in prerequisites:
164+
graph[src].append(dest)
165+
166+
traversing = set()
167+
visited = set()
168+
169+
def dfs(node):
170+
if node in traversing:
171+
return False
172+
if node in visited:
173+
return True
174+
175+
traversing.add(node)
176+
for pre in graph[node]:
177+
if not dfs(pre):
178+
return False
179+
traversing.remove(node)
180+
181+
visited.add(node)
182+
return True
183+
184+
return all(dfs(i) for i in range(numCourses))

0 commit comments

Comments
ย (0)