首先对拓扑排序,我是相当陌生的,此篇仅此记录一下有趣的拓扑排序算法思路
拓扑排序
通过简单粗暴的了解后,大致我是这么理解的:
有一处猛料 由 A选手,B选手散播出去 散播路线如下
A 告诉了 D D选手只有A的消息来源 所以选择了相信 B 告诉了 C A 又 告诉了C 那C选手也相信了这消息 那么以此类推,D 依赖于A的消息, C 依赖于 A 与 B的消息 少一个C都会怀疑此消息的真实性,这就是拓扑图带来的连带关系
207. 课程表
那么今天的主角 力扣的课程表题目就很好诠释了这种算法思想,想贴题目如下 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。 能完成所有课程返回true, 否则返回false
简明扼要的说就是:
- 一共有n门课程要上,编号从0开始
- 有先决条件[1,0] 意思就是必须先上课程0 才能上课程1,与上述所说的消息传播依赖关系类似
举个例子: n = 6, 先决条件:[1,0][2,0][3,2][4,2][5,4][5,3]
先上0课程才能上课程1,课程2
课程1 了才能上课程3
课程2 上了才能上课程4
课程4 与 课程3 都上了才能上课程5
这图专业名词应该说有向无环图,如果是有环的那说明无法完成所有课程 在这里表示的话
然后有向图又入度出度的概念
按我的理解,入度就是此时,此课程你受什么限制,比如课程0 入度就是0 因为不受限制,但是出度为2 ,出度我理解的就是限制了谁,那这里课程0 限制了课程1 与课程2
那解题思路就来了: 每次只能选入度为0的课程,如果多个为0的课程这里顺序不受限,
假设选择了课程0,那课程1入度由1变为0,课程2也是如此
接着选课程1,导致课程3入度变为0,紧接着课程5的入度也就有2变为1
接着选课程2,导致课程4入度变为0,那么课程5的入度也变为0了,至此选不到入度不为0的课程了
const canFinish = (numCourses, prerequisites) => {
const inDeg = new Array(numCourses).fill(0); // 入度数组
const map = {}; // 邻接表
for (let i = 0; i < prerequisites.length; i++) {
inDeg[prerequisites[i][0]]++; // 求课的初始入度值
if (map[prerequisites[i][1]]) { // 当前课已经存在于邻接表
map[prerequisites[i][1]].push(prerequisites[i][0]); // 添加依赖它的后续课
} else { // 当前课不存在于邻接表
map[prerequisites[i][1]] = [prerequisites[i][0]];
}
}
const queue = [];
for (let i = 0; i < inDeg.length; i++) { // 所有入度为0的课入列
if (inDeg[i] == 0) queue.push(i);
}
let count = 0;
while (queue.length) {
const selected = queue.shift(); // 当前选的课,出列
count++; // 选课数+1
const toEnQueue = map[selected]; // 获取这门课对应的后续课
if (toEnQueue && toEnQueue.length) { // 确实有后续课
for (let i = 0; i < toEnQueue.length; i++) {
inDeg[toEnQueue[i]]--; // 依赖它的后续课的入度-1
if (inDeg[toEnQueue[i]] == 0) { // 如果因此减为0,入列
queue.push(toEnQueue[i]);
}
}
}
}
return count == numCourses; // 选了的课等于总课数,true,否则false
};