diff --git a/src/main/java/org/gd/leetcode/p0207/Solution.java b/src/main/java/org/gd/leetcode/p0207/Solution.java new file mode 100644 index 00000000..0b46f0b7 --- /dev/null +++ b/src/main/java/org/gd/leetcode/p0207/Solution.java @@ -0,0 +1,118 @@ +package org.gd.leetcode.p0207; + +import org.gd.leetcode.common.LeetCode; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode.com/problems/course-schedule/ + * + * @author Horkhover Dmytro + * @since 2020-07-23 + */ +@LeetCode( + name = "Course Schedule", + difficulty = LeetCode.Level.MEDIUM, + state = LeetCode.State.FIXME, + tags = { + LeetCode.Tags.DEPTH_FIRST_SEARCH, + LeetCode.Tags.BREADTH_FIRST_SEARCH, + LeetCode.Tags.GRAPH, + LeetCode.Tags.TOPOLOGICAL_SORT + } +) +class Solution { + + public boolean canFinish(final int numCourses, int[][] arr) { + if (numCourses < 1) + throw new IllegalArgumentException(); + + Map prerequisitesMap = new HashMap<>(arr.length); + for (int[] ints : arr) { + Prerequisite p = new Prerequisite(ints[0]); + prerequisitesMap.put(p.id, p); + } + + for (int[] ints : arr) { + Prerequisite child = prerequisitesMap.get(ints[1]); + if (child != null) + prerequisitesMap.get(ints[0]).connect(child); + } + + for (int[] ints : arr) { + Prerequisite child = prerequisitesMap.get(ints[0]); + if (child.hasCycle()) + return false; + } + + return true; + } + + static class Prerequisite implements Comparable { + + private final int id; + private final Set next = new HashSet<>(); + + Prerequisite(int id) { + this.id = id; + } + + void connect(Prerequisite prerequisite) { + next.add(prerequisite); + } + + boolean hasCycle() { + return new CycleFinder(this).hasCycle(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Prerequisite)) return false; + Prerequisite that = (Prerequisite) o; + return id == that.id; + } + + @Override + public int hashCode() { return id; } + + @Override + public String toString() { + return "" + id; + } + + @Override + public int compareTo(Prerequisite o) { + return Integer.compare(id, o.id); + } + } + + static class CycleFinder { + + private final Set visited = new HashSet<>(); + private final Prerequisite head; + + CycleFinder(Prerequisite head) { + this.head = head; + } + + boolean hasCycle() { + return hasCycle(head); + } + + private boolean hasCycle(Prerequisite node) { + if (visited.contains(node)) + return true; + + visited.add(node); + for (Prerequisite next : node.next) { + if (hasCycle(next)) + return true; + } + return false; + } + } +} diff --git a/src/test/java/org/gd/leetcode/p0207/SolutionTest.java b/src/test/java/org/gd/leetcode/p0207/SolutionTest.java new file mode 100644 index 00000000..1546ca71 --- /dev/null +++ b/src/test/java/org/gd/leetcode/p0207/SolutionTest.java @@ -0,0 +1,34 @@ +package org.gd.leetcode.p0207; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Test for {@link Solution} + * + * @author Horkhover Dmytro + * @since 2020-07-23 + */ +@DisplayName("Test for org.gd.leetcode.p0207.Solution") +class SolutionTest { + + private static Stream args() { + return Stream.of( + Arguments.of(8, new int[][]{{1, 0}, {2, 6}, {1, 7}, {6, 4}, {7, 0}, {0, 5}}, true), + Arguments.of(2, new int[][]{{1, 0}, {0, 1}}, false) + ); + } + + @ParameterizedTest + @MethodSource("args") + @DisplayName("CanFinish") + void test_CanFinish(int numCourses, int[][] prerequisites, boolean expected) { + assertEquals(expected, new Solution().canFinish(numCourses, prerequisites)); + } +} \ No newline at end of file