diff --git a/0207.Course-Schedule/memo.md b/0207.Course-Schedule/memo.md new file mode 100644 index 0000000..c2f0034 --- /dev/null +++ b/0207.Course-Schedule/memo.md @@ -0,0 +1,35 @@ +# 207. Course Schedule + +## step1 + +DAG判定だと思う。トポロジカルソート。 + +step1_loop.pyを10mで書いたが遅い。おそらく set の再構築に時間がかかっているのだろう。 + +step1_backtrack.pyにすると多少速くなった。 + +ただ空間計算量が O(V^2) + +## step2 + +### 他の人のコード + +https://github.com/huyfififi/coding-challenges/pull/34 + +> NOT_VISITED_YET = 0 +> VISITING = 1 +> VISITED = 2 + +これを使って書き直すと、loopの方で速度がBeats 100%となった。 + +> トポロジカルソートの中でも特にKahn's algorithmと呼ばれるもの + +https://ja.wikipedia.org/wiki/%E3%83%88%E3%83%9D%E3%83%AD%E3%82%B8%E3%82%AB%E3%83%AB%E3%82%BD%E3%83%BC%E3%83%88 + +これを実装。個人的にはqueueを使わないもののほうがわかりやすい。 + +https://github.com/potrue/leetcode/pull/76 + +https://github.com/thonda28/leetcode/pull/3 + +https://github.com/thonda28/leetcode/pull/3 diff --git a/0207.Course-Schedule/step1_backtrack.py b/0207.Course-Schedule/step1_backtrack.py new file mode 100644 index 0000000..6e67477 --- /dev/null +++ b/0207.Course-Schedule/step1_backtrack.py @@ -0,0 +1,27 @@ +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + for a, b in prerequisites: + adjacent_list[b].append(a) + + checked: set[int] = set() + + def no_cycle(node, seen): + if node in seen: + return False + if node in checked: + return True + + seen.add(node) + for child in adjacent_list[node]: + if not no_cycle(child, seen): + return False + seen.remove(node) + checked.add(node) + return True + + for root in range(numCourses): + if not no_cycle(root, set()): + return False + + return True diff --git a/0207.Course-Schedule/step1_loop.py b/0207.Course-Schedule/step1_loop.py new file mode 100644 index 0000000..7955f9c --- /dev/null +++ b/0207.Course-Schedule/step1_loop.py @@ -0,0 +1,24 @@ +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + for a, b in prerequisites: + adjacent_list[b].append(a) + + checked: set[int] = set() + for root in range(numCourses): + if root in checked: + continue + + stack = [(root, set())] + + while stack: + node, seen = stack.pop() + if node in seen: + return False + if node in checked: + continue + checked.add(node) + for child in adjacent_list[node]: + stack.append((child, seen | {node})) + + return True diff --git a/0207.Course-Schedule/step2_kahn.py b/0207.Course-Schedule/step2_kahn.py new file mode 100644 index 0000000..ccd5a63 --- /dev/null +++ b/0207.Course-Schedule/step2_kahn.py @@ -0,0 +1,20 @@ +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + order = [0] * numCourses + for a, b in prerequisites: + adjacent_list[b].append(a) + order[a] += 1 + + nodes_with_no_parent = [node for node in range(numCourses) if order[node] == 0] + + while nodes_with_no_parent: + next_nodes_with_no_parent = [] + for node in nodes_with_no_parent: + for child in adjacent_list[node]: + order[child] -= 1 + if order[child] == 0: + next_nodes_with_no_parent.append(child) + nodes_with_no_parent = next_nodes_with_no_parent + + return sum(order) == 0 diff --git a/0207.Course-Schedule/step2_kahn_que.py b/0207.Course-Schedule/step2_kahn_que.py new file mode 100644 index 0000000..b892de0 --- /dev/null +++ b/0207.Course-Schedule/step2_kahn_que.py @@ -0,0 +1,25 @@ +from collections import deque + + +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + indegree = [0] * numCourses + + for a, b in prerequisites: + adjacent_list[b].append(a) + indegree[a] += 1 + + frontier = deque(node for node in range(numCourses) if indegree[node] == 0) + visited_count = 0 + + while frontier: + node = frontier.popleft() + visited_count += 1 + + for child in adjacent_list[node]: + indegree[child] -= 1 + if indegree[child] == 0: + frontier.append(child) + + return visited_count == numCourses diff --git a/0207.Course-Schedule/step2_loop.py b/0207.Course-Schedule/step2_loop.py new file mode 100644 index 0000000..5590aa5 --- /dev/null +++ b/0207.Course-Schedule/step2_loop.py @@ -0,0 +1,36 @@ +NOT_VISITED = 0 +VISITING = 1 +VISITED = 2 + + +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + for a, b in prerequisites: + adjacent_list[b].append(a) + + status = [0] * numCourses + for root in range(numCourses): + if status[root] == VISITED: + continue + + stack = [(root, False)] + + while stack: + node, is_visited = stack.pop() + + if is_visited: + status[node] = VISITED + continue + + if status[node] == VISITING: + return False + if status[node] == VISITED: + continue + + status[node] = VISITING + stack.append((node, True)) + for child in adjacent_list[node]: + stack.append((child, False)) + + return True diff --git a/0207.Course-Schedule/step2_recursion.py b/0207.Course-Schedule/step2_recursion.py new file mode 100644 index 0000000..4667670 --- /dev/null +++ b/0207.Course-Schedule/step2_recursion.py @@ -0,0 +1,34 @@ +NOT_VISITED = 0 +VISITING = 1 +VISITED = 2 + + +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + for a, b in prerequisites: + adjacent_list[b].append(a) + + status = [NOT_VISITED] * numCourses + + def no_cycle(node): + if status[node] == VISITING: + return False + + if status[node] == VISITED: + return True + + status[node] = VISITING + + for child in adjacent_list[node]: + if not no_cycle(child): + return False + + status[node] = VISITED + return True + + for root in range(numCourses): + if not no_cycle(root): + return False + + return True diff --git a/0207.Course-Schedule/step2_recursion_revised.py b/0207.Course-Schedule/step2_recursion_revised.py new file mode 100644 index 0000000..d4320c3 --- /dev/null +++ b/0207.Course-Schedule/step2_recursion_revised.py @@ -0,0 +1,34 @@ +NOT_VISITED = 0 +VISITING = 1 +VISITED = 2 + + +class Solution: + def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool: + adjacent_list = [[] for _ in range(numCourses)] + for a, b in prerequisites: + adjacent_list[b].append(a) + + status = [NOT_VISITED] * numCourses + + def has_cycle(node): + if status[node] == VISITING: + return True + + if status[node] == VISITED: + return False + + status[node] = VISITING + + for child in adjacent_list[node]: + if has_cycle(child): + return True + + status[node] = VISITED + return False + + for root in range(numCourses): + if has_cycle(root): + return False + + return True