【算法】【递归篇】【树】第3节:leetcode 210. 课程表 II

9 篇文章 0 订阅
8 篇文章 0 订阅

本期任务:介绍leetcode中树的几个经典问题的递归解法

【算法】【递归篇】【树】第1节:leetcode 100. 相同的树

【算法】【递归篇】【树】第2节:leetcode 105. 从前序与中序遍历序列构造二叉树

【算法】【递归篇】【树】第3节:leetcode 210. 课程表 II

【算法】【递归篇】【树】第4节:leetcode 236. 二叉树的最近公共祖先

【算法】【递归篇】【树】第5节:leetcode 572. 另一个树的子树

【算法】【递归篇】【树】第6节:leetcode 101. 对称二叉树

【算法】【递归篇】【树】第7节:leetcode 98. 验证二叉搜索树

【算法】【递归篇】【树】第8节:leetcode 102. 二叉树的层序遍历


问题来源


210. 课程表 II
现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。

可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。

示例 1:

输入: 2, [[1,0]] 
输出: [0,1]
解释: 总共有 2 门课程。要学习课程 1,你需要先完成课程 0。因此,正确的课程顺序为 [0,1] 。
示例 2:

输入: 4, [[1,0],[2,0],[3,1],[3,2]]
输出: [0,1,2,3] or [0,2,1,3]
解释: 总共有 4 门课程。要学习课程 3,你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
     因此,一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。
说明:

输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
提示:

这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
通过 DFS 进行拓扑排序 - 一个关于Coursera的精彩视频教程(21分钟),介绍拓扑排序的基本概念。
拓扑排序也可以通过 BFS 完成。


大佬解析

引入入度:当有环时,环内不存在入度为0的节点,队列会被清空而退出

  • 根据依赖关系,构建邻接表和入度数组
  • 选取入度为 0 的数据,根据邻接表,减小依赖它的数据的入度
  • 找出新入度为 0 的数据,重复第 2 步
  • 直至所有数据的入度为 0,得到排序,如果还有数据的入度没有变到 0,说明存在环形依赖

代码


class Solution:
    def findOrder(self, numCourses: int, prerequisites):
        """
        思路2:拓扑排序/BFS+入度监听
            引入入度:当有环时,环内不存在入度为0的节点,队列会被清空而退出
        
            根据依赖关系,构建邻接表和入度数组
            选取入度为 0 的数据,根据邻接表,减小依赖它的数据的入度
            找出新入度为 0 的数据,重复第 2 步
            直至所有数据的入度为 0,得到排序,如果还有数据的入度没有变到 0,说明存在环形依赖
            和 传统BFS 不一样的地方
            
            传统BFS:把出列节点的下一层子节点推入 queue,不加甄别
            拓扑排序:实施甄别和监控,新入度为 0 的先推入 queue
        """
        
        # 根据依赖关系,构建邻接表和入度数组
        relation = dict()
        indegree = [0 for i in range(numCourses)]
        for item in prerequisites:
            if relation.get(item[0]):
                relation[item[0]].append(item[1])
            else:
                relation[item[0]] = [item[1]]
            indegree[item[0]] += 1

        # 将indegree为0的节点入队
        res = list()
        queue = list()
        for i, node in enumerate(indegree):
            if node == 0:
                queue.append(i)
                indegree[i] = -1  # 避免重复入队

        while queue:
            course = queue.pop(0)
            res.append(course)

            for i in relation:
                if course in relation.get(i):
                    relation.get(i).remove(course)
                    indegree[i] -= 1
                if indegree[i] == 0:
                    queue.append(i)
                    indegree[i] = -1
        return res if len(res) == numCourses else [] # 判断是否存在环
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值