拓扑排序(LeetCode,207.课程表)

拓扑排序:拓扑排序常用来确定一个依赖关系集中,事物发生的顺序。例如,在日常工作中,可能会将项目拆分成A、B、C、D四个子部分来完成,但A依赖于B和D,C依赖于D。为了计算这个项目进行的顺序,可对这个关系集进行拓扑排序,得出一个线性的序列,则排在前面的任务就是需要先完成的任务。

将上文中的描述转化为图就是这个样子,箭头表示依赖关系,B指向A的意思就是必须先执行B在执行A,根据此图得出来的先后执行顺序就是拓扑排序,但是要注意,这个排序不是唯一的,例如可以先执行D也可以先执行B,执行完B之后可以执行A也可以执行C,只要满足图中箭头所指向的先后关系即可。例DBCA,DBAC,BDCA,BDAC都是满足图中条件的拓扑排序。

LeetCode,207.课程表

LeetCode,207.课程表

这道题就是一道考察拓扑排序的题。有numCourses门课,某些课之间有学习的先后顺序,题目问我们给出的课程安排是否合理。

prerequisites数组中保存的是课程与先修课的关系。解决这道题首先我们需要建立出来上文中的有向无环图,然后再根据图来解决拓扑排序。

当前能够执行的任务节点都有一个特点,那就是入度均为0,当该节点执行完毕之后,对应的将箭头去掉,再去寻找入度为0的节点,就是下一个可以执行的任务。当所有节点的入度均为0时意味着拓扑排序完成。

注意,能够进行拓扑排序的图中是一定没有环路的,否则会陷入循环,即拓扑排序将无法完成,所以当能排的排完后,检查一下是否所有节点入度均为0,若有节点入度不是0,则说明图中有环路存在。

综上,整体思路就是先建图,然后寻找入度为0的节点,然后将它指向的边去掉,再继续寻找入度为0的点,再将边去掉,直到最后结束。最后判断一下是否所有点入度均为0即可。

代码如下:

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {

        unordered_map<int,vector<int>> map;
        vector<int> in(numCourses);
        //建图,以邻接表方式存储每个节点
        //记录每个节点入度
        for(int i = 0;i<prerequisites.size();i++)
        {
            int a = prerequisites[i][0],b = prerequisites[i][1];
            //b为先修节点,想要完成a必须先完成b
            map[b].push_back(a);
            //将b指向a
            in[a]++;
            //a的入度加一
        }
        queue<int> q;//建立一个队列来保存当前满足条件的节点
        for(int i = 0;i<numCourses;i++)
        {
            if(in[i]==0)
            q.push(i);
        }
        //如果入度为0,则满足条件

        while(q.size())
        {
            int t =q.front();q.pop();
            for(int i = 0;i<map[t].size();i++)
            {
                in[map[t][i]]--;
                if(in[map[t][i]]==0) q.push(map[t][i]);
            }
            //将与t相连的边全部去掉,对应节点入度减1,如果入度减为0,则可以执行,加入队列中
        }

        for(int i = 0;i<numCourses;i++)
        {
            if(in[i]) return false;
        }
        //检查入度是否均为0,也能检查是否含有环路
        return true;

    }
};

关于拓扑排序简单的讲解就到这里啦,欢迎大家在评论区交流讨论,批评指正。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值