leetcode(207). Course Schedule

来源:互联网 发布:js一切皆对象 编辑:程序博客网 时间:2024/06/05 00:44

problem

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. You may assume that there are no duplicate edges in the
input prerequisites.

分析

这个问题可以抽象为判断一个图中是否有环,可以有两种算法,一种是迭代的删除所有入度为零的节点和它的边,另一种由于深度优先搜索中出现重复节点和有环是等价的,因此我们可以对图进行dfs。

#超过10%class Solution(object):    def canFinish(self, numCourses, prerequisites):        """        :type numCourses: int        :type prerequisites: List[List[int]]        :rtype: bool        """        g = {i:set() for i in range(numCourses)}        for i, j in prerequisites:            g[i].add(j)        while True:            #所有入度为零的节点            tmp = [i for i in g if not g[i]]            if not tmp:                break            for i in tmp:                #删除节点                del g[i]                for v in g.values():                    #删除边                    if i in v:                        v.remove(i)        return g == {}

树和图都可以使用dict(+set)来实现

DFS

实际上使用DFS不仅可以判断图中是否有环,还可以输出图的拓扑排序(将图转化为一维序列,使得对图中任意边uv,排序时u都出现在v之前)。

图的DFS和树的差不多,只不过在树中不会出现环,但是在图中如果出现环会无限循环,所以必须记录下已访问的节点。

class Solution(object):    def canFinish(self, numCourses, prerequisites):        """        :type numCourses: int        :type prerequisites: List[List[int]]        :rtype: bool        """        g = [[] for _ in range(numCourses)]        for a, b in prerequisites:            g[b].append(a)        visited = [0 for _ in range(numCourses)]        def dfs(node, visited):            visited[node] = 1            for v in g[node]:            #使用for保存接下来要访问的节点                if visited[v] == 1:                    return False                elif visited[v] == 0:                    if not dfs(v, visited):                        return False            visited[node] = 2            return True        for i in range(numCourses):            if visited[i] == 0:                if not dfs(i, visited):                    #print visited                    return False        return True

visit中的0表示未访问,1表示此次遍历的节点,2表示之前访问的节点,之所以有三种标记是因为防止从中间开始也可以碰到已访问节点,例如2→1→3,从1开始访问,2也会碰到已访问节点,所以使用两种标记不行。

参考文献:

https://www.cs.usfca.edu/~galles/visualization/TopoSortDFS.html
http://www.geeksforgeeks.org/depth-first-traversal-for-a-graph/
http://www.geeksforgeeks.org/topological-sorting/

原创粉丝点击