力扣CV工程师刚学的拓扑排序!包教包会!

何为拓扑排序?

所谓拓扑排序,是有向无环图才有的一种排序。对于一个给定的包含n个节点的有向图G,我们给出它的节点编号的一种排列,如果满足:
对于图G中的任意一条有向边(u,v),u在排列中都出现在v的前面。
那么就称该排列是图G的拓扑排序。
第一次读拓扑排序的概念可能会感觉难以理解,从这个概念上出发,我们需要捋清楚的问题是为什么只有有向无环图才有拓扑排序?原因很简单,如果有向无环图中拥有环,假设在环中有两点必须互相出现在各自的面前,那么就不可能存在一个满足要求的排列来作为拓扑排序。
另外,我们也可以得知,一个有向无环图的拓扑排序也可能不止一种,考虑极端情况下,整个有向无环图中只有点而没有边,那么任何一种编号排序都可以作为其拓扑排序。
在《算法》第四版中,提到了有向无环图应用广泛的模型是给定一组任务并安排它们的执行顺序,而在这类问题中最重要的一种限制条件就是优先级限制,它指明了哪些任务必须在哪些任务之前完成,比较经典的例子也就是待会举的例子中,就是课程表的例子,在修习某一门课之前,你必须拥有另外一门课的基础,这意味着你学习的课程将会有先后之分。例如在学习机器学习之前,你必须先学习Python和数据挖掘导论。同样的例子还有任务调度Java继承的类和extends关系电子表格中的单元格和公式以及符号链接的文件名和链接等。在这类问题中,我们应该把具体的任务都抽象成点,而任务的优先级限制抽象成边,从而把场景构建成一张有向无环图,利用拓扑排序来求得一个可行的解
那么如何判断一幅有向图是不是有向无环图呢?利用深度优先搜索就可以解决这个问题,由系统维护的递归调用的栈表示的正是“当前”正在遍历的有向路径,一旦我们找到了一条有向边v->w并且w已经在栈中,则说明找到了一个环。

拓扑排序例题

Leetcode 207 课程表

在这里插入图片描述
本题使用了两种不同的思想,分别是深度优先搜索DFS和广度优先搜索BFS。
广度优先搜索就是新建一个队列,然后把入度为0(也就是没有任何先修课程)的课程都加入到队列中,说明可以直接学习,也说明它们可以作为拓扑排序最前面的节点。在遍历边的时候,维护每个课程入度的同时,也要维护一个以当前课程为先修课程的课程链表,然后在队列里取出进行处理时,将该列表出来以减少课程链表中的课程的入度,如果入度为0才可加入队列中,最后只需要判断从队列中取出的课程数目和总课程数目即可知道是否有拓扑排序。如果需要拓扑排序的具体序列只需要加入一个数组记录从队列里出列的元素即可。

//广度优先搜索BFS
class Solution {
   
    public boolean canFinish(int numCourses, int[][] prerequisites) {
   
        //拓扑排序概念 有向无环图才有此排序 
        //拓扑排序:对DAG的顶点进行排序,使得对每一条有向边(u,v),均有u在v先出现。
        //代表每个元素的入度 入度为指向该顶点的边的总数
        int[] indegrees=new int[numCourses];
        //邻接表降低时间复杂度
        List<List<Integer>> adjacency=new ArrayList<>();
        Queue<Integer> queue=new LinkedList<>();
        for(int i=0;i<numCourses;i++){
   
            adjacency.add(new ArrayList<>());
        }
        for(int[] cp:prerequisites){
   
            //循环入度+1 因为学习cp[0]之前要先学习cp[1]
            indegrees[cp[0]]++;
            //邻接表 [0,1]则让1位置的链表加上0的值
            adjacency.get(cp[1]).add(cp[0]);
        }
        //取出所有入度为0的课程
        for(int i=0;i<numCourses;i++){
   
            if(indegrees[i]==0) queue.add(i);
        }
        while(!queue.isEmpty()){
   
            int pre=queue.poll();
            numCourses--;
            //从邻接表中取出入度为0的项所对应的链表 会是它完成后才能完成的课程的链表
            //当前这节课学完了 所以以当前这节课作为先修课程的课程入度都可以减一
            //当入度为0时 加入队列
            for(int cur
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值