课程表

思路

1、拓扑排序加广度优先遍历

      vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        std::unordered_map<int, std::vector<int>> map; // 字典用于记录某门前置课程,以及依赖它才能修的课程(出边)
        int sz = prerequisites.size();
        std::vector<int> pre; // 用于记录某门课程的入度,就是需要修几门必修课,才能修当前这门课。
        pre.resize(numCourses, 0);
        // 构建字典和入度表
        for(int i = 0; i < sz; ++i) {
            pre[prerequisites[i][0]] += prerequisites[i].size() - 1; // 注意这里输入数据有这种[[0,1],[0,2]],遍历的时候应该是累加
            for (int j = 1; j < prerequisites[i].size(); ++j) {
                auto iter = map.find(prerequisites[i][j]);//寻找字典中,以该门课作为前置课程,是否已经存在了。
                if (iter != map.end()) {
                    auto& tmp_vec = iter->second;//注意这里用引用,避免复制构造,可以提高性能
                    tmp_vec.push_back(prerequisites[i][0]); // 这里可以直接放入,对于两个节点之间入度边只可能有一条不会冲突
                } else {
                    std::vector<int> tmp_vec {prerequisites[i][0]};
                    map[prerequisites[i][j]] = tmp_vec;
                }
            }
        }
        // 拓扑排序
        std::queue<int> q;
        // 寻找入度为0的节点,不需要前置课程,直接放入
        for (int i = 0; i < numCourses; ++i) {
            if (0 == pre[i]) {
                q.push(i);
            }
        }
        std::vector<int> res;
        while (!q.empty()) {// 当队列为空时就结束,说明的能修的已进修过了
            int foo = q.front();
            q.pop();
            auto iter = map.find(foo);
            if(iter != map.end()) {
                auto vec = iter->second;
                for(auto& it : vec) {
                    pre[it] -= 1;
                    if (pre[it] == 0) { // 注意这里为0即可,判断条件 <=也是成立的
                        q.push(it);
                    }
                }
            }
            res.push_back(foo);
        }
        // 判断是否还有课程中存在前置课程,如果有说明该课程不能修,返回空
        for (int i = 0; i < numCourses; ++i) {
            if(pre[i] > 0) {
                return std::vector<int>();
            }
        }
        return res;
    }
posted @ 2021-07-14 10:53  cyssmile  阅读(87)  评论(0)    收藏  举报