力扣:207.课程表——拓扑排序算法

力扣:207.课程表——拓扑排序算法

1. 题目描述

你这个学期必须选修 numCourses 门课程,记为 0numCourses - 1

在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi

例如,先修课程对 [0, 1] 表示:想要学习课程 0 ,你需要先完成课程 1
请你判断是否可能完成所有课程的学习?如果可以,返回 true ;否则,返回 false

示例 1:

输入:numCourses = 2, prerequisites = [[1,0]]
输出:true
解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:

输入:numCourses = 2, prerequisites = [[1,0],[0,1]]
输出:false
解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。

提示:

1 <= numCourses <= 105
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
prerequisites[i] 中的所有课程对 互不相同

2. 拓扑序列

拓扑序列:

拓扑序列就是将 依次输出入度为0 的节点,在该节点输出后,该节点指向的节点的入度减1依次类推。

若最后输出的节点数目不等于总的节点数目,则说明图中存在环。

拓扑排序的算法步骤:

  1. 求所有顶点的入度,开辟一个顶点入度数组inDegree;
  2. 把所有入度为0的顶点入队列或栈
  3. 当栈或队列不为空时:
    1. 出栈或出队列的顶点u,输出顶点u
    2. 顶点u的所有邻接点入度减1,如果有入度为0的顶点,则入栈或队列
  4. 若此时输出的定点数小于有向图中的顶点数,则说明有向图中存在回路,否则输出的顶点顺序即为拓扑序列。

3. 题目解答

首先构造图,可以用邻接表或者邻接矩阵存放;

计算各个顶点的入度:

for(auto edge : prerequisites){
            int i = edge[0];
            int j = edge[1];
            graph[i].push_back(j);//邻接矩阵构造,
            inDegree[j]++;//入度计算
        }

入度为0的顶点入队:

for(int i = 0; i < numCourses; i++){
            if(inDegree[i] == 0){
                q.push(i);
            }
        }

逐个输出入度为0的节点(此处为计算输出节点的个数),同时将该顶点的指向的顶点的入度减1,并判断减1后是否有新的入度为0的节点产生,若有则入队,否则跳过;

while(!q.empty()){
            int top = q.front();
            q.pop();
            sum++;
            for(int i = 0; i < graph[top].size(); i++){//遍历该顶点指向的顶点
                int node = graph[top][i];
                inDegree[node]--;
                if(inDegree[node] == 0){
                    q.push(node);
                }
            }
        }

完整代码如下:

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> inDegree(numCourses,0);
        vector<vector<int>> graph(numCourses);
        
        for(auto edge : prerequisites){
            int i = edge[0];
            int j = edge[1];
            graph[i].push_back(j);
            inDegree[j]++;
        }
        queue<int> q;
        int sum = 0;
        for(int i = 0; i < numCourses; i++){
            if(inDegree[i] == 0){
                q.push(i);
            }
        }
        while(!q.empty()){
            int top = q.front();
            q.pop();
            sum++;
            for(int i = 0; i < graph[top].size(); i++){
                int node = graph[top][i];
                inDegree[node]--;
                if(inDegree[node] == 0){
                    q.push(node);
                }
            }
        }
        return sum == numCourses;
    }
};

以上的代码为邻接表解法。
力扣:207.课程表——拓扑排序算法_第1张图片

以下为邻接矩阵解法,效率过低。

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> inDegree(numCourses,0);
        vector<vector<int>> graph(numCourses,vector<int>(numCourses,0));
        
        for(auto edge : prerequisites){
            int i = edge[0];
            int j = edge[1];
            graph[i][j] = 1;
            inDegree[j]++;
        }
        queue<int> q;
        int sum = 0;
        for(int i = 0; i < numCourses; i++){
            if(inDegree[i] == 0){
                q.push(i);
            }
        }
        while(!q.empty()){
            int top = q.front();
            q.pop();
            sum++;
            for(int i = 0; i < numCourses; i++){
                if(graph[top][i] == 1){
                    inDegree[i]--;
                    if(inDegree[i] == 0){
                        q.push(i);
                    }
                }
            }
        }
        return sum == numCourses;
    }
};

临界矩阵解法效率都为5%,邻接表和邻接矩阵对比如下,第1、2为邻接表,后两个为邻接矩阵。

力扣:207.课程表——拓扑排序算法_第2张图片

你可能感兴趣的:(算法,数据结构,leetcode,动态规划,算法)