LeetCode 207:课程表(图论,利用拓扑排序判断是否有环)

题目

你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 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 <= 2000

0 <= prerequisites.length <= 5000

prerequisites[i].length == 2

0 <= ai, bi < numCourses

prerequisites[i] 中的所有课程对 互不相同

思路

利用拓扑排序来判断是否成环,如果成环的话,拓扑排序返回的节点列表的数量会少于图的节点树。所以先构建图,然后拓扑排序返回所有不成环的节点列表。

代码

第一版代码

java 复制代码
class Solution {
    public class Graph{
        public HashMap<Integer,Node> nodes;
        public HashSet<Edge> edges;
        public Graph(){
            nodes = new HashMap<>();
            edges = new HashSet<>();
        }
    }
    public class Node{
        public int value;
        public int in;
        public int out;
        public ArrayList<Node> nexts;
        public ArrayList<Edge> edges;
        public Node(int value){
            this.value = value;
            in=0;
            out=0;
            nexts = new ArrayList<>();
            edges = new ArrayList<>();
        }
    }

    public class Edge{
        public Node from;
        public Node to;
        public Edge(Node from, Node to){
            this.from = from;
            this.to = to;
        }
    }

    public HashSet<Integer> top(Graph graph){
        HashMap<Node,Integer> inMap = new HashMap<>();//一个节点对应的剩余的入度
        Queue<Node> zeroInQueue = new LinkedList<>();//存放着入度为0的节点
        for(Node node:graph.nodes.values()){
            inMap.put(node, node.in);
            if(node.in==0) zeroInQueue.add(node);
        }
        HashSet<Integer> result = new HashSet<>();//存放结果
        while(!zeroInQueue.isEmpty()){
            Node cur = zeroInQueue.poll();
            result.add(cur.value);
            for(Node next:cur.nexts){
                int newin = inMap.get(next)-1;
                inMap.put(next,newin);
                if(newin==0) zeroInQueue.add(next);
            }
        }
        return result;
    }

    public Graph createGraph(int[][] prerequisites){
        Graph graph = new Graph();
        for(int i=0;i<prerequisites.length;i++){
            int fromVal = prerequisites[i][1];
            int toVal = prerequisites[i][0]; 
            if(!graph.nodes.containsKey(fromVal)) graph.nodes.put(fromVal, new Node(fromVal));
            if(!graph.nodes.containsKey(toVal)) graph.nodes.put(toVal, new Node(toVal));
            Node fromNode = graph.nodes.get(fromVal);
            Node toNode = graph.nodes.get(toVal);
            Edge edge = new Edge(fromNode, toNode);
            fromNode.out++;
            toNode.in++;
            graph.edges.add(edge);
            fromNode.nexts.add(toNode);
            fromNode.edges.add(edge);
        }
        return  graph;
    }

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if(prerequisites.length==0) return true;
        Graph graph = createGraph(prerequisites);
        HashSet<Integer> nodes = top(graph);
        // myPrint(nodes);
        // int nums = nodes.size();
        // if(numCourses==nums||numCourses-1==nums) return true;
        HashSet<Integer> targets = new HashSet<>(); 
        for(int i=0;i<prerequisites.length;i++){
            int preVal = prerequisites[i][0];
            int laterVal = prerequisites[i][1];
            targets.add(preVal);
            targets.add(laterVal);
        }
        if(targets.equals(nodes)) return true;
        return false;
    }

    public void myPrint(HashSet<Node> nodes){
        for(Node node: nodes){
            System.out.println(node.value);
        }
    }
}

执行用时分布,17ms,击败10.60%使用 Java 的用户。

需要优化

第二版代码

感觉第一版代码得出targets列表是多余的,因为只要判断图的节点的数量和拓扑排序的不成环的节点的数量是否一致。所以top排序返回数量即可。此外,删除节点中不必要的属性。

java 复制代码
class Solution {
    public class Graph{
        public HashMap<Integer,Node> nodes;
        public HashSet<Edge> edges;
        public Graph(){
            nodes = new HashMap<>();
            edges = new HashSet<>();
        }
    }
    public class Node{
        public int value;
        public int in;
        public ArrayList<Node> nexts;
        public Node(int value){
            this.value = value;
            in=0;
            nexts = new ArrayList<>();
        }
    }

    public class Edge{
        public Node from;
        public Node to;
        public Edge(Node from, Node to){
            this.from = from;
            this.to = to;
        }
    }

    public int top(Graph graph){
        HashMap<Node,Integer> inMap = new HashMap<>();//一个节点对应的剩余的入度
        Queue<Node> zeroInQueue = new LinkedList<>();//存放着入度为0的节点
        for(Node node:graph.nodes.values()){
            inMap.put(node, node.in);
            if(node.in==0) zeroInQueue.add(node);
        }
        int ans=0;
        while(!zeroInQueue.isEmpty()){
            Node cur = zeroInQueue.poll();
            ans++;
            for(Node next:cur.nexts){
                int newin = inMap.get(next)-1;
                inMap.put(next,newin);
                if(newin==0) zeroInQueue.add(next);
            }
        }
        return ans;
    }

    public Graph createGraph(int[][] prerequisites){
        Graph graph = new Graph();
        for(int i=0;i<prerequisites.length;i++){
            int fromVal = prerequisites[i][1];
            int toVal = prerequisites[i][0]; 
            if(!graph.nodes.containsKey(fromVal)) graph.nodes.put(fromVal, new Node(fromVal));
            if(!graph.nodes.containsKey(toVal)) graph.nodes.put(toVal, new Node(toVal));
            Node fromNode = graph.nodes.get(fromVal);
            Node toNode = graph.nodes.get(toVal);
            Edge edge = new Edge(fromNode, toNode);
            toNode.in++;
            graph.edges.add(edge);
            fromNode.nexts.add(toNode);
        }
        return  graph;
    }

    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if(prerequisites.length==0) return true;
        Graph graph = createGraph(prerequisites);
        int realnum = top(graph);
        int num = graph.nodes.size();
        if(realnum==num) return true;
        return false;
    }
}

13ms,击败15.27%使用 Java 的用户。

可以看到优化的不多,毕竟自己是套的所有图基本都可以用的板子,想要优化的话就得改动这个存储数据结构,不方便自己记忆板子。但读者可以自行选择,我有提供其他数据结构实现的代码,在第三版。

第三版代码

java 复制代码
public class Solution {
    public static boolean canFinish(int numCourses, int[][] prerequisites) {
        // 构建图
        ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
        int[] inDegree = new int[numCourses];
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
        }
        for (int[] prerequisite : prerequisites) {
            int from = prerequisite[1];
            int to = prerequisite[0];
            graph.get(from).add(to);
            inDegree[to]++;
        }
        
        // 使用拓扑排序判断是否能够完成所有课程
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        int count = 0;
        while (!queue.isEmpty()) {
            int course = queue.poll();
            count++;
            for (int neighbor : graph.get(course)) {
                inDegree[neighbor]--;
                if (inDegree[neighbor] == 0) {
                    queue.offer(neighbor);
                }
            }
        }
        return count == numCourses;
    }
}

5ms,击败52.30%使用 Java 的用户。

相关推荐
蓝黑2020几秒前
Java知识点小结3:内存回收
java·gc
__AtYou__19 分钟前
Golang | Leetcode Golang题解之第417题太平洋大西洋水流问题
leetcode·golang·题解
yanyanwenmeng20 分钟前
matlab基础
开发语言·算法·matlab
Yz987620 分钟前
Hadoop里面MapReduce的序列化与Java序列化比较
java·大数据·jvm·hadoop·分布式·mapreduce·big data
##晴天小猪22 分钟前
ByteTrack多目标跟踪流程图
人工智能·算法·目标检测·机器学习·目标跟踪
凯哥Java23 分钟前
优化批处理流程:自定义BatchProcessorUtils的设计与应用
java·数据库·mysql
njnu@liyong31 分钟前
AOP-前置原理-怎么判断和拦截?
java·aop·拦截
Ddddddd_15833 分钟前
C++ | Leetcode C++题解之第421题数组中两个数的最大异或值
c++·leetcode·题解
末央&36 分钟前
【C++】内存管理
java·开发语言·c++
ly-how37 分钟前
leetcode练习 二叉树的层序遍历
算法·leetcode