0
点赞
收藏
分享

微信扫一扫

拓扑排序超详细解析+代码实战(Leetcode210:课程表||)

墨春 2022-01-10 阅读 106

文章目录


介绍

拓扑排序是将有向无环图G的所有顶点排成一个线性序列,使得对图G的任意两个顶点u,v如果存在u->v,那么在序列中u一定在v前面。这个序列又被称为拓扑排序。
譬如在实际问题中有这样的例子:
我们应该先学习c语言程序设计课程再去学数据结构课程。那么c语言程序设计即为数据结构的先导课程。对于一门课来说,必须要先学习它的先导课程才能很好地学习这门课,而且先导课程之间不能够形成环。

一、步骤详解

通过举得例子我们可以知道,如果某一门课程没有先导课程或是所有先导课程都已经学习完毕,那么这门课就可以学习了。如果有多门这样的课,它们的学习顺序任意。
其中抽象步骤如下:

  1. 定义一个队列Q,并把所有入度为0的结点加入队列
  2. 取队首结点,输出。然后删去所有从它出发的边,并令这些边到达的顶点入度减1,如果某个顶点的入度减为0,则将其加入队列。
  3. 反复进行2操作,直到队列为空。如果队列为空时入过队的节点数目恰好为N,说明拓扑排序成功,图G为有向无环图;否则,拓扑排序失败,图G中有环。

代码实现

可使用邻接表实现拓扑排序。显然,由于需要记录结点的入度,因此需要额外建立一个数组inDegree[MAXV],并在程序一开始读入图时就记录好每个结点的入度。接下来就只需要按上面所说的步骤实现即可,其伪代码如下:

vector<int> G[MAXV];//邻接表
int n,m,inDegree[MAXV];//顶点数,入度
bool topologicalSort(){
	int num=0;//记录加入拓扑序列的顶点数
	queue<int> q;
	for(int i=0;i<n;i++){
		if(inDegree[i]==0){
			q.push(i);//将所有入度为0的顶点入队
			}
		}
	while(!q.empty()){
	int u=q.front();//取队首顶点u 
	q.pop();
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i]//u的后继节点v
		inDrgree[v]--;//顶点v的入度减1 
		if(inDegree[v]==0){//顶点v的入度减为0则入队 
				q.push(v);
			} 
		}
		G[u].clear();//清空顶点u的所有出边(如无必要可不写) 
		num++;//(拓扑排序的顶点数加1) 
	}
	if(num==n)return true;//加入拓扑序列的顶点数为n,说明拓扑排序成功
	else return false;	//加入拓扑排序的顶点数小于n,说明拓扑排序失败 
}

二、实例应用

1.Leetcode:210.课程表||

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

2.思路

典型的拓扑排序问题,可套用上面模板,只不过需要将判断成功与否改成存储顺序的数组。

class Solution {
public:
    vector<int> res;//答案顺序数组
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<vector<int>> g(numCourses);//构建邻接表
        vector<int> indegree(numCourses);//入度数组
        for(const auto& it:prerequisites){
            g[it[1]].push_back(it[0]);//构建邻接表it[0]即为it[1]的前置条件
            indegree[it[0]]++;//it[0]入度+1
        }
        queue<int> q;
        for(int i=0;i<numCourses;i++){
            if(indegree[i]==0)q.push(i);//将入度为0的节点入队
        }
        while(!q.empty()){
            int temp=q.front();//取队首顶点temp
            q.pop();
            res.push_back(temp);
            for(const int& it:g[temp]){
                indegree[it]--;//删除it的前驱节点并让入度减一
                if(indegree[it]==0)q.push(it);//如果入度为0继续入队
            }
        }
        if(res.size()==numCourses)return res;//拓扑序列的顶点数为numCourses,说明拓扑排序成功
        else return {};//顶点数小于numCourses,说明存在环拓扑排序失败

    }
};
举报

相关推荐

0 条评论