leetcode(207). Course Schedule

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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值