当前位置: 首页 > news >正文

【华为机试】210. 课程表 II

文章目录

  • 210. 课程表 II
    • 描述
    • 示例 1
    • 示例 2
    • 示例 3
    • 示例 4
    • 提示
    • 解题思路
      • 方法一:Kahn算法(拓扑排序)
      • 方法二:DFS + 拓扑排序
      • 方法三:优化的Kahn算法
      • 方法四:并查集(不适用)
    • 代码实现
    • 复杂度分析
    • 测试用例
    • 完整题解代码

210. 课程表 II

描述

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

示例 1

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

示例 2

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

示例 3

输入:numCourses = 1, prerequisites = []
输出:[0]

示例 4

输入:numCourses = 3, prerequisites = [[1,0],[1,2],[0,1]]
输出:[]
解释:总共有 3 门课程。要学习课程 1,你需要先完成课程 0 和课程 2。但是要学习课程 0,你需要先完成课程 1,这形成了循环依赖,无法完成所有课程。

提示

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= numCourses * (numCourses - 1)
  • prerequisites[i].length == 2
  • 0 <= ai, bi < numCourses
  • ai != bi
  • 所有[ai, bi] 互不相同

解题思路

方法一:Kahn算法(拓扑排序)

核心思想

  • 使用Kahn算法进行拓扑排序
  • 维护每个节点的入度,从入度为0的节点开始
  • 使用队列进行BFS遍历

算法步骤

  1. 构建邻接表和入度数组
  2. 将所有入度为0的节点加入队列
  3. 从队列中取出节点,加入结果数组
  4. 更新相邻节点的入度,如果入度变为0则加入队列
  5. 检查是否所有节点都被访问

时间复杂度:O(V + E)
空间复杂度:O(V + E)

方法二:DFS + 拓扑排序

核心思想

  • 使用DFS进行拓扑排序
  • 使用三种状态标记节点:未访问(0)、访问中(1)、已访问(2)
  • 检测环的存在

算法步骤

  1. 构建邻接表
  2. 使用DFS遍历所有节点
  3. 使用状态数组检测环
  4. 按DFS完成顺序构建结果

时间复杂度:O(V + E)
空间复杂度:O(V + E)

方法三:优化的Kahn算法

核心思想

  • 优化Kahn算法的实现
  • 使用更高效的数据结构

算法步骤

  1. 构建邻接表和入度数组
  2. 使用栈或队列进行遍历
  3. 优化内存使用

时间复杂度:O(V + E)
空间复杂度:O(V + E)

方法四:并查集(不适用)

注意:并查集不适用于有向图的拓扑排序,因为拓扑排序需要保持依赖关系的方向性。

代码实现

// 方法一:Kahn算法
func findOrder1(numCourses int, prerequisites [][]int) []int {// 构建邻接表和入度数组graph := make([][]int, numCourses)inDegree := make([]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)inDegree[to]++}// 将所有入度为0的节点加入队列queue := make([]int, 0)for i := 0; i < numCourses; i++ {if inDegree[i] == 0 {queue = append(queue, i)}}result := make([]int, 0)count := 0// BFS遍历for len(queue) > 0 {current := queue[0]queue = queue[1:]result = append(result, current)count++// 更新相邻节点的入度for _, neighbor := range graph[current] {inDegree[neighbor]--if inDegree[neighbor] == 0 {queue = append(queue, neighbor)}}}if count == numCourses {return result}return []int{}
}

复杂度分析

  • 时间复杂度:O(V + E),其中V是节点数,E是边数
  • 空间复杂度:O(V + E),邻接表和队列的空间

测试用例

func main() {// 测试用例1numCourses1 := 2prerequisites1 := [][]int{{1, 0}}fmt.Printf("测试用例1: numCourses=%d, prerequisites=%v, 结果=%v\n", numCourses1, prerequisites1, findOrder1(numCourses1, prerequisites1))// 测试用例2numCourses2 := 4prerequisites2 := [][]int{{1, 0}, {2, 0}, {3, 1}, {3, 2}}fmt.Printf("测试用例2: numCourses=%d, prerequisites=%v, 结果=%v\n", numCourses2, prerequisites2, findOrder1(numCourses2, prerequisites2))// 测试用例3numCourses3 := 1prerequisites3 := [][]int{}fmt.Printf("测试用例3: numCourses=%d, prerequisites=%v, 结果=%v\n", numCourses3, prerequisites3, findOrder1(numCourses3, prerequisites3))
}

完整题解代码

package mainimport "fmt"// 方法一:Kahn算法(拓扑排序)
// 时间复杂度:O(V + E),空间复杂度:O(V + E)
func findOrder1(numCourses int, prerequisites [][]int) []int {// 构建邻接表和入度数组graph := make([][]int, numCourses)inDegree := make([]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)inDegree[to]++}// 将所有入度为0的节点加入队列queue := make([]int, 0)for i := 0; i < numCourses; i++ {if inDegree[i] == 0 {queue = append(queue, i)}}result := make([]int, 0)count := 0// BFS遍历for len(queue) > 0 {current := queue[0]queue = queue[1:]result = append(result, current)count++// 更新相邻节点的入度for _, neighbor := range graph[current] {inDegree[neighbor]--if inDegree[neighbor] == 0 {queue = append(queue, neighbor)}}}if count == numCourses {return result}return []int{}
}// 方法二:DFS + 拓扑排序
// 时间复杂度:O(V + E),空间复杂度:O(V + E)
func findOrder2(numCourses int, prerequisites [][]int) []int {// 构建邻接表graph := make([][]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)}// 状态数组:0=未访问,1=访问中,2=已访问visited := make([]int, numCourses)result := make([]int, numCourses)index := numCourses - 1// DFS遍历所有节点for i := 0; i < numCourses; i++ {if visited[i] == 0 {if !dfs(graph, i, visited, result, &index) {return []int{} // 检测到环}}}return result
}func dfs(graph [][]int, node int, visited []int, result []int, index *int) bool {visited[node] = 1 // 标记为访问中for _, neighbor := range graph[node] {if visited[neighbor] == 1 {return false // 检测到环}if visited[neighbor] == 0 {if !dfs(graph, neighbor, visited, result, index) {return false}}}visited[node] = 2 // 标记为已访问result[*index] = node*index--return true
}// 方法三:优化的Kahn算法
// 时间复杂度:O(V + E),空间复杂度:O(V + E)
func findOrder3(numCourses int, prerequisites [][]int) []int {// 构建邻接表和入度数组graph := make([][]int, numCourses)inDegree := make([]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)inDegree[to]++}// 使用栈进行遍历stack := make([]int, 0)for i := 0; i < numCourses; i++ {if inDegree[i] == 0 {stack = append(stack, i)}}result := make([]int, 0)count := 0// 栈遍历for len(stack) > 0 {current := stack[len(stack)-1]stack = stack[:len(stack)-1]result = append(result, current)count++// 更新相邻节点的入度for _, neighbor := range graph[current] {inDegree[neighbor]--if inDegree[neighbor] == 0 {stack = append(stack, neighbor)}}}if count == numCourses {return result}return []int{}
}// 方法四:改进的DFS(使用递归栈)
// 时间复杂度:O(V + E),空间复杂度:O(V + E)
func findOrder4(numCourses int, prerequisites [][]int) []int {// 构建邻接表graph := make([][]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)}// 状态数组:0=未访问,1=访问中,2=已访问visited := make([]int, numCourses)result := make([]int, numCourses)index := numCourses - 1// DFS遍历所有节点for i := 0; i < numCourses; i++ {if visited[i] == 0 {if !dfsImproved(graph, i, visited, result, &index) {return []int{} // 检测到环}}}return result
}func dfsImproved(graph [][]int, node int, visited []int, result []int, index *int) bool {visited[node] = 1 // 标记为访问中for _, neighbor := range graph[node] {if visited[neighbor] == 1 {return false // 检测到环}if visited[neighbor] == 0 {if !dfsImproved(graph, neighbor, visited, result, index) {return false}}}visited[node] = 2 // 标记为已访问result[*index] = node*index--return true
}// 方法五:使用map优化邻接表
// 时间复杂度:O(V + E),空间复杂度:O(V + E)
func findOrder5(numCourses int, prerequisites [][]int) []int {// 使用map构建邻接表graph := make(map[int][]int)inDegree := make([]int, numCourses)for _, prereq := range prerequisites {from, to := prereq[1], prereq[0]graph[from] = append(graph[from], to)inDegree[to]++}// 将所有入度为0的节点加入队列queue := make([]int, 0)for i := 0; i < numCourses; i++ {if inDegree[i] == 0 {queue = append(queue, i)}}result := make([]int, 0)count := 0// BFS遍历for len(queue) > 0 {current := queue[0]queue = queue[1:]result = append(result, current)count++// 更新相邻节点的入度for _, neighbor := range graph[current] {inDegree[neighbor]--if inDegree[neighbor] == 0 {queue = append(queue, neighbor)}}}if count == numCourses {return result}return []int{}
}func main() {fmt.Println("=== 210. 课程表 II ===")// 测试用例1numCourses1 := 2prerequisites1 := [][]int{{1, 0}}fmt.Printf("测试用例1: numCourses=%d, prerequisites=%v\n", numCourses1, prerequisites1)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses1, prerequisites1))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses1, prerequisites1))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses1, prerequisites1))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses1, prerequisites1))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses1, prerequisites1))fmt.Println()// 测试用例2numCourses2 := 4prerequisites2 := [][]int{{1, 0}, {2, 0}, {3, 1}, {3, 2}}fmt.Printf("测试用例2: numCourses=%d, prerequisites=%v\n", numCourses2, prerequisites2)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses2, prerequisites2))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses2, prerequisites2))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses2, prerequisites2))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses2, prerequisites2))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses2, prerequisites2))fmt.Println()// 测试用例3numCourses3 := 1prerequisites3 := [][]int{}fmt.Printf("测试用例3: numCourses=%d, prerequisites=%v\n", numCourses3, prerequisites3)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses3, prerequisites3))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses3, prerequisites3))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses3, prerequisites3))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses3, prerequisites3))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses3, prerequisites3))fmt.Println()// 测试用例4(有环)numCourses4 := 3prerequisites4 := [][]int{{1, 0}, {1, 2}, {0, 1}}fmt.Printf("测试用例4: numCourses=%d, prerequisites=%v\n", numCourses4, prerequisites4)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses4, prerequisites4))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses4, prerequisites4))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses4, prerequisites4))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses4, prerequisites4))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses4, prerequisites4))fmt.Println()// 额外测试用例numCourses5 := 3prerequisites5 := [][]int{{0, 1}, {0, 2}, {1, 2}}fmt.Printf("额外测试: numCourses=%d, prerequisites=%v\n", numCourses5, prerequisites5)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses5, prerequisites5))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses5, prerequisites5))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses5, prerequisites5))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses5, prerequisites5))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses5, prerequisites5))fmt.Println()// 边界测试用例numCourses6 := 0prerequisites6 := [][]int{}fmt.Printf("边界测试: numCourses=%d, prerequisites=%v\n", numCourses6, prerequisites6)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses6, prerequisites6))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses6, prerequisites6))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses6, prerequisites6))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses6, prerequisites6))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses6, prerequisites6))fmt.Println()// 复杂测试用例numCourses7 := 6prerequisites7 := [][]int{{1, 0}, {2, 1}, {3, 2}, {4, 3}, {5, 4}}fmt.Printf("复杂测试: numCourses=%d, prerequisites=%v\n", numCourses7, prerequisites7)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses7, prerequisites7))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses7, prerequisites7))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses7, prerequisites7))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses7, prerequisites7))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses7, prerequisites7))fmt.Println()// 无依赖测试用例numCourses8 := 4prerequisites8 := [][]int{}fmt.Printf("无依赖测试: numCourses=%d, prerequisites=%v\n", numCourses8, prerequisites8)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses8, prerequisites8))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses8, prerequisites8))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses8, prerequisites8))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses8, prerequisites8))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses8, prerequisites8))fmt.Println()// 自环测试用例numCourses9 := 2prerequisites9 := [][]int{{0, 0}}fmt.Printf("自环测试: numCourses=%d, prerequisites=%v\n", numCourses9, prerequisites9)fmt.Printf("方法一结果: %v\n", findOrder1(numCourses9, prerequisites9))fmt.Printf("方法二结果: %v\n", findOrder2(numCourses9, prerequisites9))fmt.Printf("方法三结果: %v\n", findOrder3(numCourses9, prerequisites9))fmt.Printf("方法四结果: %v\n", findOrder4(numCourses9, prerequisites9))fmt.Printf("方法五结果: %v\n", findOrder5(numCourses9, prerequisites9))
}
http://www.dtcms.com/a/302283.html

相关文章:

  • Kernel PWN 入门(二)
  • 【深度学习优化算法】10:Adam算法
  • Mybatis_4
  • 设计模式十二:门面模式 (FaçadePattern)
  • Kafka——请求是怎么被处理的?
  • 6.2 总线事务和定时 (答案见原书 P295)
  • 疏老师-python训练营-Day28类的定义和方法
  • 【LeetCode 热题 100】35. 搜索插入位置——二分查找(闭区间)
  • 区块链分叉原理与代码仿真
  • leetcode 2044. 统计按位或能得到最大值的子集数目 中等
  • 主要分布于内侧内嗅皮层的层Ⅲ的网格-速度联合细胞(Grid × Speed Conjunctive Cells)对NLP中的深层语义分析的积极影响和启示
  • 热斑漏检率↓78%!陌讯多模态算法在无人机光伏巡检的轻量化实践
  • 问题大全【1】
  • 【深度解析】R语言与作物模型(以DSSAT模型为例)融合应用
  • 散点图(散点矩阵)相关介绍
  • 【计算机科学与应用】基于多域变换的视频水印嵌入算法研究
  • 电脑出现英文字母开不了机怎么办 原因与修复方法
  • 开发笔记 | 实现人物立绘的差分效果
  • 达梦有多少个模式
  • 低成本嵌入式Linux开发方案:通过配置文件实现参数设置
  • LeetCode 68:文本左右对齐
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘papermill’问题
  • Docker搭建Hadoop集群
  • 进程管理(systemd)
  • 企业微信服务商创建第三方应用配置数据回调url和指令回调url的java代码实现
  • Python系统交互库全解析
  • Entity Framework Core (EF Core) 中Database
  • 归雁思维:解锁自然规律与人类智慧的桥梁
  • Online Proofing System(OPS)在线校样系统使用说明
  • Linux进程概念(五)进程地址空间