@@ -56,3 +56,104 @@ def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
5656 queue .append (neighbor )
5757
5858 return completed == numCourses
59+
60+ """
61+ 위상 정렬을 구하는 알고리즘
62+ 1. DFS 기반 (스택을 쌓아 뒤집는 방식)
63+ 2. Kahn 알고리즘 (진입차수 0부터 차례로 큐 처리)
64+
65+ Kahn 알고리즘이란?
66+ 방향 그래프에서 진입차수(indegree)가 0인 정점부터 차례로 제거(큐에 넣어 꺼내기)하며 위상 순서를 만드는 방법.
67+ 진입차수가 0인 정점이 하나도 없는데 아직 남은 정점이 있다면 사이클 존재 -> 위상 정렬 불가.
68+
69+ 위상 순서: 방향 그래프에서 간선(u→v)이 있다면, 순서에서 u가 항상 v보다 앞에 나오는 정점들의 나열.
70+ 진입차수: 정점으로 들어오는 간선의 개수.
71+
72+
73+ TC: O(V + E), V: 과목 수, E: prerequisite 관계 수
74+ SC: O(V + E), 그래프 + 진입차수 배열
75+ """
76+ class Solution :
77+ def canFinish (self , numCourses : int , prerequisites : List [List [int ]]) -> bool :
78+ # 1) 그래프(인접 리스트)와 진입차수 배열 만들기
79+ adj = [[] for _ in range (numCourses )]
80+ indeg = [0 ] * numCourses
81+ for a , b in prerequisites : # b -> a (b를 먼저 들어야 a를 들을 수 있음)
82+ adj [b ].append (a )
83+ indeg [a ] += 1
84+
85+ # 2) 진입차수 0인 정점들로 큐 초기화
86+ q = deque ([i for i in range (numCourses ) if indeg [i ] == 0 ])
87+ taken = 0 # 처리(수강) 완료한 과목 수
88+
89+ # 3) 큐에서 빼며 간선 제거(=후속 과목 진입차수 감소)
90+ while q :
91+ u = q .popleft ()
92+ taken += 1
93+ for v in adj [u ]:
94+ indeg [v ] -= 1
95+ if indeg [v ] == 0 :
96+ q .append (v )
97+
98+ # 4) 모두 처리됐으면 사이클 없음
99+ return taken == numCourses
100+
101+
102+ """
103+ DFS 기반
104+ TC: O(V + E), V: 과목 수, E: prerequisite 관계 수
105+ SC: O(V + E), 그래프 + 탐색 중인 과목 집합 + 수강 완료한 과목 집합
106+ """
107+ class Solution :
108+ def canFinish (self , numCourses : int , prerequisites : List [List [int ]]) -> bool :
109+ graph = {i : [] for i in range (numCourses )}
110+ for crs , pre in prerequisites :
111+ # 선수 과목을 원소로 추가
112+ graph [crs ].append (pre )
113+
114+ # 탐색중
115+ traversing = set ()
116+ # 수강가능한 과목
117+ finished = set ()
118+
119+ def can_finish (crs ):
120+ if crs in traversing :
121+ return False
122+ if crs in finished :
123+ return True
124+
125+ traversing .add (crs )
126+ for pre in graph [crs ]:
127+ if not can_finish (pre ):
128+ return False
129+ traversing .remove (crs )
130+ finished .add (crs )
131+ return True
132+
133+ for crs in graph :
134+ if not can_finish (crs ):
135+ return False
136+ return True
137+
138+ from functools import cache
139+
140+ # 줄인 코드
141+ class Solution :
142+ def canFinish (self , numCourses : int , prerequisites : List [List [int ]]) -> bool :
143+ graph = {i : [] for i in range (numCourses )}
144+ for crs , pre in prerequisites :
145+ graph [crs ].append (pre )
146+
147+ traversing = set ()
148+
149+ @cache
150+ def can_finish (crs ):
151+ if crs in traversing :
152+ return False
153+
154+ traversing .add (crs )
155+ result = all (can_finish (pre ) for pre in graph [crs ])
156+ traversing .remove (crs )
157+ return result
158+
159+ return all (can_finish (crs ) for crs in graph )
0 commit comments