207. Course Schedule (Medium)

There are a total of n courses you have to take, labeled from 0 to n - 1.

Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1]

Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses?

For example:

2, [[1,0]]

There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible.

2, [[1,0],[0,1]]

There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

Note:

The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.

Hints:

  1. This problem is equivalent to finding if a cycle exists in a directed graph. If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses.
  2. Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort.
  3. Topological sort could also be done via BFS.

Solution 1: Topological Sort, BFS

我们定义二维数组graph来表示这个有向图,一位数组in来表示每个顶点的入度。我们开始先根据输入来建立这个有向图,并将入度数组也初始化好。然后我们定义一个queue变量,将所有入度为0的点放入队列中,然后开始遍历队列,从graph里遍历其连接的点,每到达一个新节点,将其入度减一,如果此时该点入度为0,则放入队列末尾。直到遍历完队列中所有的值,若此时还有节点的入度不为0,则说明环存在,返回false,反之则返回true。代码如下:

bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
    vector<vector<int>> graph(numCourses, vector<int>());
    vector<int> in(numCourses, 0);
    for (auto a: prerequisites) {
        graph[a.second].push_back(a.first);
        ++in[a.first];
    }

    queue<int> q;
    for (int i = 0; i < numCourses; ++i) {
        if (in[i] == 0) q.push(i);
    }

    while (!q.empty()) {
        int front = q.front(); q.pop();
        for (auto a: graph[front]) {
            if (--in[a] == 0) q.push(a);
        }
    }

    for (int i = 0; i < numCourses; ++i) {
        if (in[i] != 0) return false;
    }
    return true;
}

Solution 2: DFS

For DFS, it will first visit a node, then one neighbor of it, then one neighbor of this neighbor... and so on. If it meets a node which was visited in the current process of DFS visit, a cycle is detected and we will return false. Otherwise it will start from another unvisited node and repeat this process till all the nodes have been visited. Note that you should make two records: one is to record all the visited nodes and the other is to record the visited nodes in the current DFS visit.

下面我们来看DFS的解法,也需要建立有向图,还是用二维数组来建立,和BFS不同的是,我们像现在需要一个一维数组visit来记录访问状态,大体思路是,先建立好有向图,然后从第一个门课开始,找其可构成哪门课,暂时将当前课程标记为已访问,然后对新得到的课程调用DFS递归,直到出现新的课程已经访问过了,则返回false,没有冲突的话返回true,然后把标记为已访问的课程改为未访问。代码如下:

class Solution {
    bool canFinishDFS(vector<vector<int>>& graph, vector<int>& visited, int i) {

        if (visited[i] == -1) return false; // circle occur on current search path
        if (visited[i] == 1) return true; // this node has been searched before without cycle

        // not visited yet
        // set to -1: search right now
        visited[i] = -1;
        for (auto a: graph[i]) {
            if (!canFinishDFS(graph, visited, a)) return false;
        }
        // finish searching current node
        visited[i] = 1;
        return true;
    }

public:
    bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<vector<int>> graph(numCourses, vector<int>()); //neighboor
        vector<int> visited(numCourses, 0);

        for (auto a: prerequisites) {
            graph[a.second].push_back(a.first);
        }

        for (int i = 0; i < numCourses; ++i) {
            if (!canFinishDFS(graph, visited, i)) return false;
        }

        return true;
    }
};

results matching ""

    No results matching ""