05.拓扑排序

目录

  • 一.拓扑排序简介
  • 二。leetcode207题课程表1
    • 1.分析
    • 2.题解BFS&&拓扑排序
  • 三。leetcode210题课程表2
    • 1.分析
    • 2.题解BFS&&拓扑排序

一.拓扑排序简介

刷题的时候碰到拓扑排序的问题的时候一脸懵逼,不知道啥是拓扑排序,看了下官方简介,如下:
05.拓扑排序_第1张图片
还是蒙蔽是不是?
其实通俗的讲拓扑排序就是一个有向的图且图中不存在环,这就能形成一个拓扑排序。一般来说这类问题描述都是一个任务在完成之前要完成另一个任务,这就能构成一个有向图判断是否有环的问题。
其实这种是有规律的,下面说下拓扑排序的步骤:
1.先是构建一个图,可以看到每个节点有指向此节点的线段,有指出的线段,指向的称为入度,指出的称为出度,下面的图将每个节点的入度都标上。
05.拓扑排序_第2张图片
2.将入度为0的节点剥离,先剥离2这个节点,可以看到节点剥离后,3的入度更新为了1
05.拓扑排序_第3张图片
3.接下来0和1节点同理,都剥离,然后将其相应的下一个节点的入度更新
05.拓扑排序_第4张图片
4.最后剥离4和3节点,最终5节点入度为0,如果最终的所有节点入度都为0了那么就构成了拓扑排序。
05.拓扑排序_第5张图片

下面看下不构成拓扑排序的是什么样的。
1.每个节点上都标上入度
05.拓扑排序_第6张图片
2.最终剥离后会形成下面的图,可以看到,没有入度为0的可以剥离了,形成的最终的图不是每个节点的入度都为0所以不构成拓扑排序。
05.拓扑排序_第7张图片

其实上面的步骤就判断了一个图是否有环,有环的图最终形成的图的节点肯定有节点的入度不为0。

二。leetcode207题课程表1

在学习完拓扑排序后具体看下应用。
05.拓扑排序_第8张图片

1.分析

一看这题目在完成某之前需要完成什么,很自然想到可以用拓扑排序来解决问题。

2.题解BFS&&拓扑排序

算法步骤很简单:
1.将边缘列表转为邻接表。
2.BFS,不断将入度为0的节点放入queue中,然后更新入度表。
3.最终如果只有入度为0的节点,那么就返回true,如果有入度不为0的就返回false。
注邻接表:就是用hashmap构造key为前驱节点,对应的value是后续节点列表,每次假装删除了当前节点,那么后续节点列表的入度表就更新(减去1)

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        //因为涉及到拓扑排序,首先需要一个邻接表和记录出度或者入度的列表【a,b】,这儿是b→a.就是先学的指向后学的,那么入度为0的就是要拿出来的
        Map<Integer,List<Integer>> graph = new HashMap<>();
        int[] inDegree = new int[numCourses];
        for(int i = 0;i<prerequisites.length;i++){
            int aft = prerequisites[i][0];
            int bef = prerequisites[i][1];
            //说明aft节点多了一个bef,多了个入度
            inDegree[aft]=inDegree[aft]+1;
            if(graph.containsKey(bef)){
                graph.get(bef).add(aft);
            }else{
                List<Integer> list = new ArrayList<>();
                list.add(aft);
                graph.put(bef,list);
            }
        }
        Queue<Integer> queue = new LinkedList<>();
        for(int i=0;i<numCourses;i++){
            if(inDegree[i]==0){
                queue.offer(i);
            }
        }
        while(!queue.isEmpty()){
            int node = queue.poll();
            List<Integer> l = graph.get(node);
            //如果l等于null了,就没有用empty()这个方法的可能了
            if(l!=null){
                for(int i = 0;i<l.size();i++){
                    inDegree[l.get(i)]=inDegree[l.get(i)]-1;
                    if(inDegree[l.get(i)]==0){
                        queue.offer(l.get(i));
                    }
                }
            }

        }

        for(int i = 0;i<numCourses;i++){
            if(inDegree[i]!=0){
                return false;
            }
        }
        return true;


    }
}

三。leetcode210题课程表2

05.拓扑排序_第9张图片

1.分析

和207题目类似,就是把每次入度要拿走的节点放入result中,最终返回出来。因为题目提的要求就是记录下上课顺序,那么就在每次poll出来的时候将这个节点放入result就好了,因为每次放入queue的就是入度为0的节点,而每次删除的就是queue里面poll出来的节点。

2.题解BFS&&拓扑排序

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        //用一个数组去记录入度为0的节点,他的总长度就是课的数目,每个位置都代表对应的课程数目
        int[] inDegree = new int[numCourses];

        //首先需要将边缘列表转换为带方向的邻接表,注意数组里面0是后修课,1是先修课,以先修课为key,在这是先修课指向后修课,1指向0,所以统计入度的时候统计的是0的入度
        Map<Integer,List<Integer>> graph = new HashMap<>();
        for(int i=0;i<prerequisites.length;i++){
            //每次遍历一个数组的时候,这个二维数组都会有一个前驱节点和后驱节点,而这儿统计的是入度数,目前是0指向1,所以统计的是0的入度
            inDegree[prerequisites[i][0]]++;
            if(graph.containsKey(prerequisites[i][1])){
                graph.get(prerequisites[i][1]).add(prerequisites[i][0]);
            }else{
                List<Integer> list = new ArrayList<>();
                list.add(prerequisites[i][0]);
                graph.put(prerequisites[i][1],list);
            }
        }

        //进行Bfs,每次将一个入度为0的点抹去
        Queue<Integer> queue = new LinkedList<>();

        //将入度为0的节点删除,相应的入度表更新
        
        //1.先将入度为0的节点添加到队列里面
        for(int i=0;i<numCourses;i++){
            if(inDegree[i]==0){
                queue.add(i);
            }
        }
        int[] result = new int[numCourses];
        int j = 0;
        while(!queue.isEmpty()){
            //每次poll出来的这个节点,其实就是入度为0的点,我们要将其删除,入度为0的节点删除的话,其后续节点对应的入度数就会减去1
            int cur = queue.poll();
            result[j] = cur;
            j++;
            List<Integer> toTake = graph.get(cur);
            for(int i=0;toTake!=null&&i<toTake.size();i++){
                inDegree[toTake.get(i)]--;
                //在这个后续列表的某个节点入度减去1后,这个点入度如果等于0的话就将其放入queue队列。
                if(inDegree[toTake.get(i)]==0){
                    queue.add(toTake.get(i));
                }                
            }

        }

        if(j!=numCourses){
            return new int[0];
        }
        return result;
    }
}

你可能感兴趣的:(算法)