Course Schedule解题心得
问题链接:https://leetcode.com/problems/course-schedule/description/
问题描述
给定总课程数与课程的两两优先关系,判断是否能够在保证课程先后学习顺序的情况下完成所有课程
解题思路
将所有的课程视为一个一个的节点,视为图的一部分,假设课程
a
优先于课程
由此可见一定需要先选择完成那些没有其他课程优先于该课程的课程节点,即优先级最高的节点,由图像分析可知,优先级最高的节点一定就是图的源点,即入度为零的点;于是这个问题装换为拓扑排序问题
拓扑排序: 就是每次取图中一个入度为零的点,加入排序队列尾,并在图中删除,并且更新那些该节点指向的点,再重复该过程,直到图中没有入度为零的点
通过拓扑排序,如果最后拓扑算法运行结束后,图中还有剩余的点,则说明这些课程无法找到一个序列使得优先次序互不冲突;如果图中没有剩余的节点,则这些课程都能上完
拓扑排序算法实现
每当对一个算法的实现时,自己都没有很好的优化实现的效果,这一次我也是进行了两次拓扑排序算法的实现方法:一个是自己通过自己理解,自己构建的,一个是通过学习拓扑排序优秀的实现实现的
个人的算法实现
- 获取邻接表,同时存储各个节点的入度
- 每次遍历节点入度表,查看入度为零的下标,通过邻接表再次更新入读表,再用一个标识数组,表示该节点已经移出图像
- 重复2过程,直到没有入度为零而仍在图中的点,拓扑结束
class Solution {
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> nodeGraph[numCourses];
int *nodeLevel=new int[numCourses];
memset(nodeLevel, 0, sizeof(int)*numCourses);
for (int i=0; i<prerequisites.size(); i++){
nodeGraph[prerequisites[i].first].push_back(prerequisites[i].second);
nodeLevel[prerequisites[i].second]++;
}
bool update=false;
int nLeft=numCourses;
do{
//find nodeLevel[i]==0; update;
update=false;
for (int i=0; i<numCourses; i++){
if (nodeLevel[i]==0){
update=true;
nodeLevel[i]--;
nLeft--;
for (int j=0; j<nodeGraph[i].size(); j++){
if (nodeLevel[nodeGraph[i][j]]!=-1){
nodeLevel[nodeGraph[i][j]]--;
}
}
}
}
}while(update);
delete []nodeLevel;
return nLeft==0;
}
};
- 评价
- 算法实现复杂,对于入度为零的节点的获取是通过线性搜索的方法得当,时间复杂度偏高
- 过程复杂,涉及使用数组记录节点是否在图像中
- 通过一个数字记录移出图的节点数,从而实现判断图中是否仍有节点剩余
优化后的算法
- 通过栈存储入度为零的节点
- 每次取出栈顶的节点,再在更新指向节点的过程中将新的入度为零的点加入栈
class Solution{
public:
bool canFinish(int numCourses, vector<pair<int, int>>& prerequisites) {
vector<int> nodeGraph[numCourses];
int *nodeLevel=new int[numCourses];
memset(nodeLevel, 0, sizeof(int)*numCourses);
for (int i=0; i<prerequisites.size(); i++){
nodeGraph[prerequisites[i].first].push_back(prerequisites[i].second);
nodeLevel[prerequisites[i].second]++;
}
int finished=0;
stack<int> node;
for (int i=0; i<numCourses; i++){
if (nodeLevel[i]==0)
node.push(i);
}
while(!node.empty()){
int top=node.top();
node.pop();
finished++;
for (int i=0; i<nodeGraph[top].size(); i++){
int son=nodeGraph[top][i];
nodeLevel[son]--;
if (nodeLevel[son]==0) node.push(son);
}
}
delete []nodeLevel;
return finished==numCourses;
}
};
- 评价:
- 通过栈的实现,使得搜索的时间复杂度变为 O(1) ,原来的复杂度为 O(n)
- 节省了下标空间的存储
算法优化结果
代码地址:https://github.com/zhanzongyuan/leetcode/blob/master/207_Course%20Schedule.cpp