题目
现在你总共有
numCourses
门课需要选,记为0
到numCourses - 1
。
给你一个数组prerequisites
,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。
例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回 任意一种 就可以了。
如果不可能完成所有课程,返回 一个空数组 。
提示
- 1 <= numCourses <= 2000
- 0 <= prerequisites.length <= numCourses * (numCourses - 1)
- prerequisites[i].length == 2
- 0 <= ai, bi < numCourses
- ai != bi
- 所有[ai, bi] 互不相同
题解
第一次做这种类似的题,发现刚开始自己想的思路不能说完全不对,只能说全靠堆内存来实现,结果发现并不可取,然后我就看了看题解,发现是需要用到拓扑排序
,刚开始接触是懵的
首先要知道什么是 入度
假设有 [1,0],[2,0],[3,1],[3,2],[4,5],[4,3],[5,0],[5,1] 这样一些数据
根据题目的意思,这个拓扑图还是有向的
这里的可以理解为入度=当前待学的前置课程数量
知道了入度是什么,接着就需要了解一下拓扑排序可以如何进行
1、从所有待学的课程中找到当前入度=0的课程进行学习并记录
2、然后再将本轮学习的课程所相关连的课程的入度进行重新计算
3、重复1、2两个步骤,直到学完所有课程
下面是通过代码实现上面的逻辑
func findOrder(numCourses int, prerequisites [][]int) []int {
mResult := make([]int, 0)
// 存放所要学习课程的入度
mMap := make(map[int]int)
// 以 前置课程Key 需要这个前置的相关课程Value
mMapV := make(map[int][]int)
// 根据题目中第一句可以知道,要学的课程一定为从0到numCourses - 1
// 所以这里初始化要学习的课程,为后面每个课程的入度做准备
for i := 0; i < numCourses; i++ {
mMap[i] = 0
}
// 这里的遍历有2个作用
// 首先是计算出来所有课程的入度是多少
// 其次是将前置课程所相关的课程收集在一起,方便后面对课程的入度做调整(因为所有的条件都不相等,如果有相等的就不能这样直接无脑加进去了)
for _, v := range prerequisites {
mMap[v[0]]++
mSlice, mOk := mMapV[v[1]]
if !mOk {
mSlice = make([]int, 0)
}
mSlice = append(mSlice, v[0])
mMapV[v[1]] = mSlice
}
// 这里的循环就是为了学完所有的课程,如果条件出现“死锁”的条件,就要直接跳出去
for len(mMap) > 0 {
// 这个计数是为了判断本轮循环是否有 入度=0 的课程进行学习的
mCnt := 0
// 这里就是遍历map查找入度=0的课程
for k, v := range mMap {
if v == 0 {
mResult = append(mResult, k)
delete(mMap, k)
mCnt++
}
}
// 这里就是检查是否会出现条件“死锁”,这里的 mCnt 一旦为0
// 就必定是还有需要学习的课程,但是所有待学习的课程入度没有为0的
if mCnt == 0 {
return []int{}
}
// 根据 mCnt 可以知道本轮学习了几门课程,然后从数组中找到学习的对应可成,然后再计算一下想关联课程的入度
for i := 1; i <= mCnt; i++ {
mNum := mResult[len(mResult)-i]
mSlice, mOk := mMapV[mNum]
if mOk {
for _, v := range mSlice {
mMap[v]--
}
}
}
}
return mResult
}
提交记录
这次的结题结果不是很理想,还需提升,将这次的结果保存下来是因为感觉这次的结题思路比较容易理解,方便以后查阅理解
题目来源:力扣题库
一点点笔记,以便以后翻阅。