LeetCode——课程表 i-ii

Q:你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们:[0,1]
给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

示例 1:
输入: 2, [[1,0]]
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:
输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
 
提示:
输入的先决条件是由 边缘列表 表示的图形,而不是 邻接矩阵 。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。\(1 <= numCourses <= 10^5\)

A:
这个题说那么多,实际上就是判断有没有环
1.BFS:队列模拟拓扑排序。

public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (numCourses <= 1 || prerequisites.length <= 1)
            return true;
        int[] rudu = new int[numCourses];//存入度
        for (int[] prerequisite : prerequisites) {
            rudu[prerequisite[1]]++;
        }
        Queue<Integer> q = new LinkedList<>();//队列里存的是入度为0的点
        for (int i = 0; i < numCourses; i++) {
            if (rudu[i] == 0)
                q.add(i);
        }
        if (q.isEmpty())
            return false;
        int count = 0;
        while (!q.isEmpty()) {
            int temp = q.poll();
            count++;
            for (int[] prerequisite : prerequisites) {
                if (prerequisite[0] == temp) {
                    int curr = prerequisite[1];
                    rudu[curr]--;//删除该入度为0的点时,
                    if (rudu[curr] == 0)
                        q.add(curr);
                }
            }
        }
        return count == numCourses;
    }

2.DFS
这里设计一个flag:

  1. flag = 0,未被读取过;
  2. flag = -1,当前路径读取,如果当前路径再次读取,说明有环;
  3. flag = 1,之前被别的路径读取过,不需要再读取了,这步是剪枝的过程。
    private int[] flag;

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (numCourses <= 1 || prerequisites.length <= 1)
            return true;
        flag = new int[numCourses];
        Arrays.fill(flag, 0);
        for (int[] prerequisite : prerequisites) {
            if (flag[prerequisite[0]] == 0) {//未被读取过
                boolean f = DFS(prerequisite[0], prerequisites);
                if (!f)
                    return false;
            }
        }
        return true;
    }

    private boolean DFS(int i, int[][] prerequisites) {
        flag[i] = -1;//当前路径被读取
        boolean f = true;
        for (int[] prerequisite : prerequisites) {
            if (prerequisite[0] == i) {
                if (flag[prerequisite[1]] == 0) {//从来未被读取
                    f = DFS(prerequisite[1], prerequisites);
                } else if (flag[prerequisite[1]] == -1) {//当前路径又一次被读取
                    f = false;
                }
            }
            if (!f)
                return false;
        }
        flag[i] = 1;//进入下一次之前,这已经变成前一次的路径了
        return true;
    }

Q:现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。

示例 1:
输入: 2, [[1,0]]
输出: [0,1]
解释: 总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
示例 2:
输入: 4, [[1,0],[2,0],[3,1],[3,2]]
输出: [0,1,2,3] or [0,2,1,3]
解释: 总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
  因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。
说明:
输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。

A:
同为拓扑排序。
1.BFS

public int[] findOrder(int numCourses, int[][] prerequisites) {
        int[] result = new int[numCourses];
        if (numCourses <= 1)
            return new int[numCourses];
        int[] rudu = new int[numCourses];
        for (int[] prerequisite : prerequisites) {
            rudu[prerequisite[1]]++;
        }
        Queue<Integer> q = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (rudu[i] == 0)
                q.add(i);
        }
        if (q.isEmpty())
            return new int[0];
        int count = numCourses-1;
        while (!q.isEmpty()) {
            int temp = q.poll();
            result[count] = temp;
            count--;
            for (int[] prerequisite : prerequisites) {
                if (prerequisite[0] == temp) {
                    int curr = prerequisite[1];
                    rudu[curr]--;
                    if (rudu[curr] == 0)
                        q.add(curr);
                }
            }
        }
        if (count != -1)//注意count需要判断是否为-1
            return new int[0];
        return result;
    }

2.DFS

    private int[] flag;
    private ArrayList<Integer> res;

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        if (numCourses <= 1)
            return new int[numCourses];
        res = new ArrayList<>();
        flag = new int[numCourses];
        Arrays.fill(flag, 0);
        for (int i = 0; i < numCourses; i++) {
            if (flag[i] == 0) {//这里和前面不一样,是因为如果有独立的点,一定要放进去,所以每个点都要读
                boolean f = DFS(i, prerequisites);
                if (!f)
                    return new int[0];
            }
        }
        int[] result = new int[numCourses];
        for (int i = 0; i < numCourses; i++) {
            result[i] = res.get(i);
        }
        return result;
    }

    private boolean DFS(int i, int[][] prerequisites) {
        flag[i] = -1;
        boolean f = true;
        for (int[] prerequisite : prerequisites) {
            if (prerequisite[0] == i) {
                if (flag[prerequisite[1]] == 0) {
                    f = DFS(prerequisite[1], prerequisites);
                } else if (flag[prerequisite[1]] == -1) {
                    f = false;
                }
            }
            if (!f)
                return false;
        }
        flag[i] = 1;
        res.add(i);//这样才能逆着放入结果中
        return true;
    }
posted @ 2020-05-09 15:12  Shaw_喆宇  阅读(213)  评论(0编辑  收藏  举报