207. 课程表

题目

207. 课程表

我的思路

经过分析,本题似乎等价于判断一个图中是否存在环。我的朴素思路是,先把图用数据结构表示起来,然后依次对每个节点深搜,当路径长于图中的节点数时,说明存在环。

在实现上有许多地方可以改进,值得推敲。首先我在实现时是用链表表示有向图的,可以用更适合图(而非树)的邻接表来表示。其次我在深搜时,没有进行记忆化处理。最后,在判断是否存在环时用路径长度作为判断标准效率也很低。

下面整理一下官方题解的思路。

首先本题等价于判断是否存在“拓扑排序”问题。而由拓扑排序相关算法的性质,等价于寻找一个“拓扑排序”的问题。这类问题是有套路和模板的。

不论深搜还是广搜,核心思路是不断地寻找并访问入度为0的节点(安全节点)。

思路一:深搜

所有的节点有三种状态,未访问,访问过,和正在访问。当搜索到正在访问的节点时说明有环。

递归思路:把当前访问的节点设置为正在访问状态,遍历当前节点指向的所有节点,若遍历完毕,把当前节点设为已访问。尽管此题目不需要,但是可以在标记节点为已访问时,把该节点入栈,这个栈可以作为一种拓扑排序!

class Solution {
private:
    vector<vector<int>> edges;
    vector<int> visited;
    bool valid = true;

public:
    void dfs(int u) {
        visited[u] = 1;
        for (int v: edges[u]) {
            if (visited[v] == 0) {
                dfs(v);
                if (!valid) {
                    return;
                }
            }
            else if (visited[v] == 1) {
                valid = false;
                return;
            }
        }
        visited[u] = 2;
    }

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info: prerequisites) {
            edges[info[1]].push_back(info[0]);
        }
        for (int i = 0; i < numCourses && valid; ++i) {
            if (!visited[i]) {
                dfs(i);
            }
        }
        return valid;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

思路二:广搜(非递归)

广搜需要从入度为0,也就是拓扑排序中可以可以直接作为首元素的节点开始搜索。用一个辅助数组记录所有节点的入度数。

循环:把入度为0的节点加入队列,取队首,消除与队首有关的边(把辅助数组中相关节点的入度数-1)。取队首时可以把它加入结果队列,最后形成一个“拓扑排序”。出循环的条件是直到队列为空,比较结果队列的长度和节点数即可得知是否存在“拓扑排序”。

class Solution {
private:
    vector<vector<int>> edges;
    vector<int> indeg;

public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        indeg.resize(numCourses);
        for (const auto& info: prerequisites) {
            edges[info[1]].push_back(info[0]);
            ++indeg[info[0]];
        }

        queue<int> q;
        for (int i = 0; i < numCourses; ++i) {
            if (indeg[i] == 0) {
                q.push(i);
            }
        }

        int visited = 0;
        while (!q.empty()) {
            ++visited;
            int u = q.front();
            q.pop();
            for (int v: edges[u]) {
                --indeg[v];
                if (indeg[v] == 0) {
                    q.push(v);
                }
            }
        }

        return visited == numCourses;
    }
};

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/course-schedule/solution/ke-cheng-biao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我的实现

class Solution {
public:
    struct TreeNode{
    int val;
    vector<TreeNode*> Children;
    TreeNode(int v):val(v),Children(){};
};
    bool search(TreeNode *presentRoot,int count,const int & max){
        if(count>max){
            return false;
        }
        for(auto it: presentRoot->Children)
        {
            if(false==search(it,count+1,max))return false;
        }
        return true;

    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<TreeNode*> allcourses;
        TreeNode *temp;
        for(int i=0;i<numCourses;i++)
        {
            temp = new TreeNode(i);
            allcourses.push_back(temp);
        }
        for(auto it: prerequisites)
        {
            //cout<<it[0]<<it[1]<<endl;
            (allcourses[it[0]]->Children).push_back(allcourses[it[1]]);
        }
        for(auto it: allcourses)
        {
            if(search(it,0,numCourses)==false)return false;
        }
        return true;
    }
};

/*
好像就是等效于检测是否存在环(a,b) (b..,..z) (z,a)
深搜?
struct TreeNode{
    int val;
    vector<TreeNode*> Children;
    TreeNode(v):val(v),Children():{};
}
1.为prerequisites中出现的匹配建造一些树
2.深搜每一个非叶子节点,若出现环,那么返回false;如果没有环,那么true
*/

拓展学习

拓扑排序

邻接表

原文地址:https://www.cnblogs.com/BoysCryToo/p/13432520.html