207. Course Schedule

2021-02-09  本文已影响0人  jluemmmm

课程选修问题,在修一些课程之前需要完成其他课程的学习,确认是否能够完成所有课程的学习。

经典的拓扑排序问题,给定一个包含n个节点的有向图 G,给出节点编号的一种排序,如果满足:
对于图G中的任意一条有向边(u, v),u在排列中都出现在v的前面,该排列是图G的拓扑排序

将本题建模成求拓扑排序的问题

深度优先搜索

用一个栈来存储所有已搜索完成的节点

对于一个节点u,如果它的所有相邻节点已经搜索完成,那么在搜索回溯到u的时候,u本身会变成一个搜索完成的节点。这里的相邻节点是通过u出发通过一条有向边可以到达所有节点。

假设当前搜索到了节点u,如果它的相邻节点都已经搜索完成,那么这些节点已经在栈中了,此时就可以把u入栈。此时从栈顶向栈底的顺序看,由于u处于栈顶的位置,那么u出现在所有的相邻节点的前面。因此对于u这个节点而言,是满足拓扑顺序要求的。

对图进行一次深度优先搜索。每个节点进行回溯的时候,我们把该节点放入栈中。最终从栈顶到栈底的序列就是一种拓扑排序。

算法

对于图中的任意一个节点,它在搜索过程中有三种状态:

通过上述的三种状态,给出使用深度优先搜索得到拓扑排序的算法流程,在每一轮的搜索开始时,任取一个未搜索的节点进行深度优先搜索。

在整个深度优先搜索的过程中,如果没有找到环,那么栈中存储的数据,从栈顶到栈底的顺序为拓扑排序

复杂度

/**
 * @param {number} numCourses
 * @param {number[][]} prerequisites
 * @return {boolean}
 */
var canFinish = function(numCourses, prerequisites) {
    let index = 0
    let visited = Array(numCourses).fill(0)
    let G = Array(numCourses).fill(0).map( _ => [])
    
    for(let e of prerequisites) {
        G[e[1]].push(e[0])
    }
    
    for(let i = 0; i < numCourses ; i++) {
        if(visited[i] === 0) {
            dfs(i)
        }
    }
    
    return index === numCourses ? true : false
    
    function dfs(u) {
        visited[u] = 1
        for(let v of G[u]) {
            if(!visited[v]) {
                dfs(v)
            } else if(visited[v] === 1) {
                return false
            }
        }
        visited[u] = 2
        index++
    }
};

广度优先搜索

DFS 是一种逆向思维,最先被放入栈中的节点是在拓扑排序中最后面的节点。使用正向思维,顺序地生成拓扑排序更为直观。

拓扑排序中最前面的节点,该节点不会有任何入边,它没有任何先修课程要求,当将一个节点加入答案中后,就可以移除它的所有出边,代表它的相邻节点少了一门先修课程要求。如果某个相邻节点变成了没有任何入边的节点,代表这门课可以开始学习了。按照这样的流程,不断将没有入边的节点加入答案,直到答案中包含所有的节点或者不存在没有入边的节点(图中包含环)

算法

使用一个队列进行广度优先搜索,开始时,所有入度为0的节点都被放入队列中,它们可以作为拓扑排序最前面的节点,并且它们之间的相对顺序无关

在广度优先搜索的每一步中,取出队首的节点u:

在广度优先搜索的过程中,如果没有找到图中的环,那么栈中存储的这所有 n 个节点,从栈顶到栈底的顺序即为一种拓扑排序。

/**
 * @param {number} numCourses
 * @param {number[][]} prerequisites
 * @return {boolean}
 */
var canFinish = function(numCourses, prerequisites) {
    let index = 0
    let inDegree = Array(numCourses).fill(0)
    let G = Array(numCourses).fill(0).map( _ => [])
    
    for (let e of prerequisites) {
        G[e[1]].push(e[0])
        inDegree[e[0]]++
    }
    
    while(index < numCourses) {
        let hasCycle = true
        for(let i = 0; i < numCourses; i++) {
            if(inDegree[i] === 0) {
                index++
                inDegree[i] = -1
                hasCycle = false
                
                for(let w of G[i]) {
                    inDegree[w]--
                }
            }
        }
        if(hasCycle) return false
    }
    return numCourses === index ? true : false
};
上一篇下一篇

猜你喜欢

热点阅读