看到依赖问题,首先想到把问题转化成 有向图 这种数据结构,如果图中有环,证明存在依赖循环
这里记录,用图,和拓扑排序的思想来解决的两道题目。用DFS和BFS都可以做,但是BFS代码更为简洁,这里写了BFS代码
一、课程表 207
1、分析
由于课程与课程之间存在依赖问题,我们将其转化为图。用图去记录课程与课程之间的关系,而且我们选择邻接表的形式记录。且将被依赖项作为起始点指向目标项。如a依赖b,我们在建图的时候是b指向a。 记录每个节点的入度,如果入度为0,证明不依赖任何项。我们首先将入度为0的节点加入队列初始化。然后开始执行BFS循环,从队列中弹出一个节点,减少相邻节点的入度,同时新产生入度为0的节点加入队列中。重复上述过程,如果能够遍历到所有节点,证明没有环的存在。否则有环。
具体见代码。
2、代码
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
graph,incount = {},{}
for i in range(numCourses):
graph[i] = []
incount[i] = 0
for t,f in prerequisites:
graph[f] += [t]
incount[t] += 1
print(graph)
que = collections.deque()
for i in range(numCourses):
if incount[i]==0:
que.append(i)
count = 0
while que:
temp = que.popleft()
count += 1
for next in graph[temp]:
incount[next] -= 1
if incount[next]==0:
que.append(next)
return count==numCourses
JS
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {boolean}
*/
var canFinish = function(numCourses, prerequisites) {
let graph={},incount={};
for(let i=0;i<numCourses;i++){
graph[i] = [];
incount[i] = 0;
}
for(item of prerequisites){
let t=item[0],f=item[1];
graph[f] = [...graph[f],t];
incount[t] ++;
}
let count=0,que=[];
for(let i=0;i<numCourses;i++){
if(incount[i]===0){
que.push(i);
}
}
while(que.length!=0){
let temp = que.shift();
count ++;
for(let next of graph[temp]){
incount[next] --;
if(incount[next]==0){
que.push(next);
}
}
}
return count===numCourses;
};
二、课程表II 210
1、分析
这道题与上一道题的区别在于要记录遍历的过程,其实这个遍历就是一个拓扑排序的结果。具体见代码和注释。
2、代码
class Solution:
def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
# 用res记录最终结果
res = []
# graph记录生成图
graph = {}
# incount用于记录每个节点的入度
incount = {}
# 初始化邻接表和入度记录表
for i in range(numCourses):
graph[i] = []
incount[i] = 0
# 遍历依赖关系,生成图,以及统计节点入度
for t,f in prerequisites:
graph[f] += [t]
incount[t] += 1
# 利用双端队列记录初始化队列中的节点
que = collections.deque()
# 入度为零表示没有其他依赖,可以作为拓扑排序的起点
for i in range(numCourses):
if incount[i]==0:
que.append(i)
# 记录遍历节点的个数
count = 0
while que:
temp = que.popleft()
count += 1
# 弹出节点的顺序就是拓扑排序的结果
res.append(temp)
for next in graph[temp]:
incount[next] -= 1
if incount[next] == 0:
que.append(next)
# 如果存在环,拓扑排序将不存在
if count!=numCourses:
return []
return res
JS
/**
* @param {number} numCourses
* @param {number[][]} prerequisites
* @return {number[]}
*/
var findOrder = function(numCourses, prerequisites) {
let res = [],graph={},incount ={};
for(let i=0;i<numCourses;i++){
graph[i] = [];
incount[i] = 0;
}
for(let item of prerequisites){
let f=item[1],t=item[0];
graph[f] = [...graph[f],t];
incount[t] ++;
}
let count=0,que=[];
for(let i=0;i<numCourses;i++){
if(incount[i]===0){
que.push(i);
}
}
while(que.length!=0){
let temp = que.shift();
count ++;
res.push(temp);
for(let next of graph[temp]){
incount[next] --;
if(incount[next]===0){
que.push(next);
}
}
}
if(count!=numCourses){
return [];
}
return res;
};