leetcode----207. Course Schedule

链接:

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

大意:

给定一个整数num为所需学习的课程数目,一个二维整数数组pre为修习课程的先决条件。若pre中某个一维数组为[0,1],则表示要学习0号课程,则必须先学习1号课程。规定:课程编号为0~num-1。现在要求判断是否有一种安排使得可以修习完所有课程。例子:

思路:

说的这么多,其实就是在一个有向图中判断是否存在拓扑序列。

通过num值,构造一个每个节点的入度数组degree,以及构造一个以每个节点为边的起点的边信息graph(其实就是构图)。

通过pre数组,对degree和graph进行初始化。

接下来就是模拟寻找拓扑序列了。

首先,找到图中剩余顶点中的一个入度为0的顶点,之后将以该顶点为起点的边所指向的节点的入度减1,同时需要将该节点置为已访问(不然下次找入度为0的顶点时又会找到它)。之后便是重复该过程。知道找到的顶点数(课程数)等于num(返回true)或者找不到入度为0的点了 (返回false)

代码:

class Solution {
    // 判断一个图是否可以找出一个拓扑序列
    public boolean canFinish(int num, int[][] pre) {
        int[] degree = new int[num]; // 记录每个节点的入度
        List<Integer>[] graph = new ArrayList[num]; // 记录各个边的关系
        for (int i = 0; i < num; i++) {
            graph[i] = new ArrayList<>();
        }
        for (int[] nums : pre) {
            degree[nums[0]]++;
            graph[nums[1]].add(nums[0]); // nums[1] -> nums[0]
        }
        int count = 0;
        boolean[] visited = new boolean[num]; // 记录节点是否已被访问
        while (count < num) {
            int curIdx = -1;
            // 尝试找到一个入度为0的点
            for (int i = 0; i < degree.length; i++) {
                if (!visited[i] && degree[i] == 0) {
                    curIdx = i;
                    break;
                }
            }
            // 没找到入度为0的点
            if (curIdx == -1)
                break;
            visited[curIdx] = true;
            for (int tail : graph[curIdx]) {
                degree[tail]--;
            }
            count++;
        }
        return count == num;
    }
}

结果:

结论:

效率有点低,需要改进。

改进:

猜测主要性能瓶颈是在while循环中尝试找到入度为0的点的for循环中,需要遍历一次degree数组。可以使用一个列表idxs首先存储一开始入度为0的点,然后依次取出list的点,判断该点指向的那些点入度减1后是否为0.若为0,则将指向的该点也加入idxs

当idxs为空时跳出循环,并判断count是否与num相等

class Solution {
    // 判断一个图是否可以找出一个拓扑序列
    public boolean canFinish(int num, int[][] pre) {
        int[] degree = new int[num]; // 记录每个节点的入度
        List<Integer>[] graph = new ArrayList[num]; // 记录各个边的关系
        for (int i = 0; i < num; i++) {
            graph[i] = new ArrayList<>();
        }
        for (int[] nums : pre) {
            degree[nums[0]]++;
            graph[nums[1]].add(nums[0]); // nums[1] -> nums[0]
        }
        int count = 0;
        ArrayList<Integer> idxs = new ArrayList<>(); // 存储所有当前入度为0的点的位置
        // 记录入度为0的点
        for (int i = 0; i < degree.length; i++) {
            if (degree[i] == 0) 
                idxs.add(i);
        }
        while (!idxs.isEmpty()) {
            int curIdx = idxs.remove(idxs.size() - 1);
            for (int tail : graph[curIdx]) {
                if (--degree[tail] == 0) // 又找到一个新的入度为0的点
                    idxs.add(tail);
            }
            count++;
        }
        return count == num;
    }
}

 

猜你喜欢

转载自blog.csdn.net/smart_ferry/article/details/89509991