LeetCode 207 Medium 课程选修问题 Python

方法一:

    算法:深搜

    思路

        题中很明显地提示了这是一个图的结构

        对图进行遍历,如果图中有环则一定不能完成,否则可以完成

        因此将本题转化为判断给定的图中是否含环的问题

 

        深搜遍历图的话,如何判断有环?

            在遍历过程中,如果从某节点出发深度遍历,某时刻又遍历回本节点,则说明图中有环!

        因此:

            1. 首先构建图的深度优先遍历的整体框架,构建图的结构

            2. 构建visitied列表记录节点是否被访问

            3. 设定节点三个状态

                0:未被访问

                -1:正在被访问,即正在从该节点出发向下递归深度遍历

                1:该节点已被访问过

            4. 从某节点开始深度遍历,遍历时先置该节点状态为-1,如果后序节点中都没有重复访问本节点

               的visit状态,说明没有环,才可以将该节点visit置1,visit[i]=1

               所以递归函数的一开始要先判断当前节点的状态是否为-1,即visit[i] == -1,如果为-1则应该

               返回False,说明当前正在访问的节点已经是上一层中正在访问的节点了!

 

    注意

        1. 在递归的过程中,如果visit[i]==1说明已经访问过了,注意,这里的已经访问过和存在环的重复访问

           的已经访问过不同!如果==1说明肯定是没问题的访问,直接return True就好,它对应的情况是指那种

           因为main函数外面的for循环导致先访问的节点会置True,后面再访问时已经True了,比如样例:

            2,[[0,1]],图结构:1-->0,由于外面的for循环,会先访问节点0,然后置visit[0]=1,而后面

            从1开始访问到0的时候,就已经知道0的visit状态是1就直接返回Ture就好,事实上如果还是有点混淆

            的话,不写这句也可以,写上只是加快运行罢了

                    if visit[node.val] == 1:

                        return True

        2. 注意这种递归的返回方式,要将底层的递归bool结果返回,可以在if中判断,根据if的结果在return False

         or True

        3. 注意dfs中的循环里的条件,不用判断visit[neightbor] ==0,否则的话将无法到达有环的那个节点!

        因为有环的话,下一个节点的状态可能就是-1,如果只判断==0才往下递归,那永远return的是True!

        所以就直接向下递归就好了,如果有环就判断是-1,return False,正常的话,就正常的返回True并且将后面

        的节点的vist状态置1

    复杂度分析

        时间:ON,遍历所有节点

        空间:ON,递归栈空间,辅助建立图,以及visit的空间

def canFinish( numCourses, prerequisites):

    visit = [0] * numCourses

    class GraphNode:

        def __init__(self, x):

            self.val = x

            self.neighbors = []

    graph = [GraphNode(i) for i in range(numCourses)]

    for pair in prerequisites:

        graph[pair[1]].neighbors.append(graph[pair[0]])

    def dfs(node):

        if visit[node.val] == -1:

            return False

        if visit[node.val] == 1:

            return True

        visit[node.val] = -1

        for neighbor in node.neighbors:

            if not dfs(neighbor):

                return False

        visit[node.val] = 1

        return True

    for node in graph:

        node_notvisited = visit[node.val] == 0

        if node_notvisited and not dfs(node):

            return False

    return True


      实际上上面的代码是参照这个代码构建出来的,上面代码是用本解法在我的思路和数据结构的模式下构建出来的

        这里的解法对图的结构进行了简化,即用二维列表带代表图的结构,下标代表图的val,graph[i]对应图中的一个

        节点,每个节点都是一个列表,和我上面自己写的GraphNode是相同的作用,但是这样写更简化
 

def canFinish1( numCourses, prerequisites):

    graph = [[] for _ in range(numCourses)]

    visit = [0 for _ in range(numCourses)]

    for x, y in prerequisites:

        graph[y].append(x)
    def dfs(i):

        if visit[i] == -1:

            return False

        if visit[i] == 1:

            return True

        visit[i] = -1

        for j in graph[i]:

            if not dfs(j):

                return False

        visit[i] = 1

        return True

    for i in range(numCourses):

        if visit[i] == 0 and not dfs(i):

            return False
    return True

方法二:

    算法:拓扑排序/宽度优先搜索

    思路:

        拓扑排序在判断DAG有向无环图中,本来就是可以检测图中是否有环存在的一种方法,其思想如下:

            1. 构建一个图,计算好所有节点的入度

            2. 将入度为0的节点依次从图中拆解下来,拆解入度为0的节点时同时拆解掉以他为起始点的边

            3. 更新剩余节点的入度,选择入度为0的点回到第2步继续上述过程,直到整个图中没有入度为0的点为止

            如果都拆完后所有节点都拆掉了,即没有剩余节点,则这个图内无环,否则有环

        所以对比此题:

            1. 构件图,用列表记录所有节点的入度

            2. 建立一个队列queue记录入度为0的节点

            3. 拆解入度为0的节点-->从queue中弹出节点

                 将弹出节点的neighbors的入度 -= 1 (拆解边)

                 若-=1后入度为0则入队列

                 记录队列中弹出元素的counter += 1

            4. 判断counter是否等于原节点数,若相等,则无环,否则有环

    复杂度分析:

        时间:ON,遍历所有节点

        空间:ON,构建图,入度列表什么的空间

def canFinish2(numCourses, prerequisites):

    graph = [[] for i in range(numCourses)]

    indegree = [0] * numCourses

    for x, y in prerequisites:

        indegree[x] += 1

        graph[y].append(x)

    queue = []

    k = 0

    for i in range(numCourses):

        if indegree[i] == 0:

            queue.append(i)

    while queue:

        node = queue.pop(0)

        k += 1

        for i in graph[node]:

            indegree[i] -= 1

            if indegree[i] == 0:

                queue.append(i)

    return k == numCourses

    My Method --> 拓扑排序

    和上面的canFinish2一样的思路,只不过用了我的数据结构GraphNode

def canFinish3(numCourses, prerequisites):

    class GraphNode:

        def __init__(self, x):

            self.val = x

            self.neighbors = []

    graph = [GraphNode(i) for i in range(numCourses)]

    indegrees = [0] * numCourses

    for end, begin in prerequisites:

        indegrees[end] += 1

        graph[begin].neighbors.append(graph[end])

    queue = []

    for i in range(len(indegrees)):

        if indegrees[i] == 0:

            queue.append(i)

    count = 0

    while queue:

        node_index = queue.pop(0)

        node = graph[node_index]

        count += 1

        for neighbor in node.neighbors:

            indegrees[neighbor.val] -= 1

            if indegrees[neighbor.val] == 0:

                queue.append(neighbor.val)

    return count == numCourses

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值