leetcode-via-go 207. 课程表(拓扑排序 dfs bfs)

本文通过Go语言探讨LeetCode 207题,研究如何判断在给定课程的先决条件下是否能完成所有课程的学习。文章介绍拓扑排序的概念,并提供了两种解决方法:深度优先搜索(DFS)和广度优先搜索(BFS)。通过拓扑排序判断图中是否存在环,并以此确定课程选修的可行性。

因为6.824要用go,所以打算用go刷一阵子题,熟悉一下语言,过阵子做的时候好上手

另一方面也是好久没有认真刷题了 熟练度噌噌的掉

你这个学期必须选修 numCourse 门课程,记为 0 到 numCourse-1 。

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

给定课程总量以及它们的先决条件,请你判断是否可能完成所有课程的学习?

 

示例 1:

输入: 2, [[1,0]] 
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成​课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
 

提示:

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

在有向图中,优先级限制下的调度问题等价于拓扑排序问题,所以本题主要是运用拓扑排序算法

本题要问的是是否可行,通过判断有无环 如果没环,拓扑排序能否排到所有的元素(突然想到,DAG一定是可以拓扑排序的吗?

有两种做法,bfs,dfs

bfs:

const (
	src = 1
	dst = 0
)

//  原理是判断是多源的搜索能否遍历整个图,当然首先要确保有一个源(不是环)
// 使用map 作为邻接表
// 问题在于:每次mark的时候 要保证他没有其他入度了 如果是单纯的搜索意味着要不断的修改count
func canFinish(numCourses int, prerequisites [][]int) bool {
	out := make(map[int][]int)
	in := make(map[int][]int)
	for i := 0; i < len(prerequisites); i++ {
		out[prerequisites[i][src]] = append(out[prerequisites[i][src]], prerequisites[i][dst])
		in[prerequisites[i][dst]] = append(in[prerequisites[i][dst]], prerequisites[i][src])
	}
	var source []int
	inCount := make([]int, numCourses)
	for i := 0; i < numCourses; i++ {
		if len(in[i]) == 0 {
			source = append(source, i)
		}
		inCount[i] = len(in[i])
	}
	if len(source) == 0 {
		return false
	}
	marked := make([]bool, numCourses)
	for {
		if len(source) == 0 {
			break
		}
		s := source[0]
		source = source[1:]
		marked[s] = true
		for _, v := range out[s] {
			inCount[v]--
		}
		for _, v := range out[s] {
			if marked[v] == false && inCount[v] == 0 {
				source = append(source, v)
			}
		}
	}
	for _, v := range marked {
		if v == false {
			return false
		}
	}
	return true
}

dfs:

这里有一个结论:拓扑排序就是reverse dfs

const (
	src = 1
	dst = 0
)

func dfs(out map[int][]int, marked []bool, start int, rs *[]int, onStack []bool, hasCycle *bool) {
	if marked[start] == true {
		return
	}
	marked[start] = true
	onStack[start] = true
	for _, v := range out[start] {
		if *hasCycle == true {
			return
		}
		if onStack[v] == true {
			*hasCycle = true
		}
		dfs(out, marked, v, rs, onStack, hasCycle)
	}
	onStack[start] = false
	*rs = append(*rs, start)
}

//  原理是判断是多源的搜索能否遍历整个图,当然首先要确保有一个源(不是环)
// 使用map 作为邻接表
// 问题在于:每次mark的时候 要保证他没有其他入度了 如果是单纯的搜索意味着要不断的修改count
func canFinish(numCourses int, prerequisites [][]int) bool {
	out := make(map[int][]int)
	in := make(map[int][]int)
	for i := 0; i < len(prerequisites); i++ {
		out[prerequisites[i][src]] = append(out[prerequisites[i][src]], prerequisites[i][dst])
		in[prerequisites[i][dst]] = append(in[prerequisites[i][dst]], prerequisites[i][src])
	}
	var source []int
	inCount := make([]int, numCourses)
	for i := 0; i < numCourses; i++ {
		if len(in[i]) == 0 {
			source = append(source, i)
		}
		inCount[i] = len(in[i])
	}
	if len(source) == 0 {
		return false
	}
	marked := make([]bool, numCourses)
	var rs []int
	onStack := make([]bool, numCourses)
	hasCycle := false
	for _, s := range source {
		dfs(out, marked, s, &rs, onStack, &hasCycle)
	}
	if hasCycle == true {
		return false
	}
	return len(rs) == numCourses
}

这两种做法都是算法4里提到过的,正文讲到的是reverse dfs,习题里提到的是bfs

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值