Leetcode -- Course Schedule II

https://leetcode.com/problems/course-schedule-ii/description/

其实这一题是Course Schedule I https://leetcode.com/problems/course-schedule/description/ 的延长版。因为那一题我一次过就做出来了暂时我就不放解了。简单来说思想就是通过有向关系去构建图,然后从图中寻找是否存在环。这一题就是如果图中并不存在环,就返回此图的拓扑排序。


关于什么是拓扑排序,请看http://blog.csdn.net/dm_vincent/article/details/7714519 。 事实上也就是一个叶子结点到根节点的一个序列,在这个序列里叶子结点永远在他的父亲节点或者祖上节点的前面。根据这个特性,拓扑排序关于一幅图是可以存在不止一个序列。


这一题的第一步和Course Schedule是一样的,都是构建图。在这里我只是简单的用了HashMap和HashSet的混用来表达节点和边。HashMap的key是节点本身,value是一个HashSet,表示的是指向哪一个节点,也就是边的set。所以先构图。因为拓扑排序是一个从叶子结点到跟节点的序列。所以需要下面两种东西的其中一种

1. 所有的叶子结点,以及可以从叶子结点回溯到根节点的路径

2.或者所有的根节点,以及可以从根节点回溯到叶子结点的路径。

基本上dfs和bfs都可以解决问题。当然,回到这一题来说,解决方案也有很多种。这里给出一种dfs的,和一种bfs的

dfs的基本思路就是从根节点往叶子结点进行一个bottom-up的操作。先回到这一题解释一下根节点和叶子结点分别是什么,根节点就是这门课不是任何一门课的preresiquite, 叶子结点就是这门课没有任何的preresiquite。

我们先从根节点开始往下找进行一个go for bottom的过程,找到环,就返回空集合表示遇到了环,然后我们不停往上返回空集合以示我们遇到了一个错误的情况。当遇到了叶子结点,我们就开始bottom up的过程,我们不停返回当前层级的节点并往list后面加,因为在这个bottom里面,叶子结点是最先被返回并加到结果集合里的,然后儿子节点总是比父亲节点先被返回并且加到父亲节点的前面。至此,拓扑排序就成立了。

因为这个图可能包含不止一个根节点,所以我们可能需要做几次的dfs操作去把所有结果回溯。同时,为了避免重复操作,一旦我们开始返回一个节点并加入到结果里,我们就把它从图中删除,从实际意义上就表示我们已经把这门课给修了,避免以后别的需要这门课作为prerequisite的课再回溯一次这个节点,就没有必要了,因为一旦这门课被回溯到并且加入结果,就表示这门课以及这门课的直接和间接的prerequisites都已经被学过了。具体代码等待会儿讲完bfs一起放出。


bfs的思路就有些相反了。如果说dfs的思路是根据 2 来的(因为可以bottom up), bfs的思路就是根据 1 来的。我们从叶子结点出发,先把所有不需要prerequisite的课都学了(放进queue里),然后看看再能学什么课(检查一下哪些课的prerequisite被全部学完了,再放进queue里)。。如此往下走,直到所有课被学完或者没有课可以再学了。如果所有课都被学完我们就返回结果,否则就表示实际遇到环了,返回空结集。

下面给出代码(包含了bfs和dfs两种):

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        Map<Integer, Set<Integer>> preReqMap = new HashMap<Integer, Set<Integer>>();
        Map<Integer, Set<Integer>> postReqMap = new HashMap<Integer, Set<Integer>>();
        
        for (int[] preq : prerequisites) {
            if (!preReqMap.containsKey(preq[0])) {
                preReqMap.put(preq[0], new HashSet<Integer>());
            }
            
            if (!postReqMap.containsKey(preq[1])) {
                postReqMap.put(preq[1], new HashSet<Integer>());
            }
            
            preReqMap.get(preq[0]).add(preq[1]);
            postReqMap.get(preq[1]).add(preq[0]);
        }
        
//         int[] result = new int[numCourses];
//         int curIndex = 0;
//         Set<Integer> visited = new HashSet<Integer>();
//         for (int i = 0; i < numCourses; i++) {
//             // case 1 : single node without prerequisite
//             if (!preReqMap.containsKey(i) && !postReqMap.containsKey(i)) {
//                 result[curIndex] = i;
//                 curIndex++;
//             } else if (!postReqMap.containsKey(i)) {
//                 // case 2: Root node.
//                 List<Integer> curRes = this._DFSSearchGraph(preReqMap, new HashSet<Integer>(), i);
//                 if (curRes.isEmpty()) return new int[0];
//                 for (Integer resCourse : curRes) {
//                     if (!visited.contains(resCourse)) {
//                         visited.add(resCourse);
//                         result[curIndex] = resCourse;
//                         curIndex++;
//                     }
//                 }
//             }
//         }
                       
//         return curIndex == numCourses ? result : new int[0];
        
        return _BFSSearchGraph(preReqMap, postReqMap, numCourses);
    }
    
    private int[] _BFSSearchGraph(Map<Integer, Set<Integer>> preReqMap, Map<Integer, Set<Integer>> postReqMap, int num) {
        Queue<Integer> canLearn = new LinkedList<Integer>();
        
        int[] res = new int[num];
        int curIndex = 0;
        for (int i = 0; i < num; i++) {
            if (!preReqMap.containsKey(i)) {
                canLearn.add(i);
            }
        }
        
        while (!canLearn.isEmpty()) {
            int learnt = canLearn.poll();
            res[curIndex] = learnt;
            curIndex++;
            if (postReqMap.containsKey(learnt)) {
                Set<Integer> leadTo = postReqMap.get(learnt);
                for (Integer leadCourse : leadTo) {
                    preReqMap.get(leadCourse).remove(learnt);
                    if (preReqMap.get(leadCourse).isEmpty()) {
                        preReqMap.remove(leadCourse);
                        canLearn.add(leadCourse);
                    }
                }
            }
        }
        
        return curIndex == num ? res : new int[0];
    }
    
    private List<Integer> _DFSSearchGraph(Map<Integer, Set<Integer>> preReqMap, Set<Integer> visited, int course) {
        if (visited.contains(course)) {
            return new LinkedList<Integer>();
        } else {
            visited.add(course);
            LinkedList<Integer> res = new LinkedList<Integer>();
            if (preReqMap.containsKey(course)) {
                Set<Integer> reqs = preReqMap.get(course);
                for (Integer req : reqs) {
                    List<Integer> nextRes = _DFSSearchGraph(preReqMap, visited, req);
                    if (nextRes.isEmpty()) return nextRes;
                    
                    for (Integer resCourse : nextRes) {
                        res.add(resCourse);
                    }
                }
                
                preReqMap.remove(course);
            }
            visited.remove(course);
            res.add(course);
            return res;
        }
    }


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值