[leetcode] CourseSchedule

CourseSchedule

  • 问题描述:输入一个n代表我们有N门课程,编码成0-n-1, 然后给一系列课程的前置条件,问这样安排课程是否合理。
  • 问题可以转化成判断图中是否有环的问题。
    • 我们将每门课程看成一个节点。
    • 前置条件是条有向的边。
    • 所有图是有向图。
    • 如果图中存在环则前置课程必定存在冲突,否则则不冲突。
  • 是否存在环?
    • 解法1
      • 我们首先计算每个节点的入度。
      • 将所有入度为0的节点加入队列中
      • 依次遍历队列中的节点, 队列为空则退出
      • 针对每个节点,我们删除其所有边,并且对应节点的入度减1。
      • 如果最后所有节点都遍历了,则没有环,否则有环
    • 解法2
      • 我们利用dfs来遍历。
      • 我们针对每个点有三个状态0,1,2
        • 0 代表这个点还没访问
        • 1 代表正在访问,当前连通分量还没有访问结束,如果碰到将要访问的点是1,则代表有环
        • 2 代表该点已经访问过,且该点所在的连通分量也访问完成。
//
// Created by 梁栋 on 2019-05-12.
//
#include <vector>
#include <iostream>
using namespace std;
class Solution {
public:
    bool canFinishBase(int** graph, int n, int cur, int* visit, int* father){
        // 判断是否存在环
        visit[cur] = 1;
        for(int i=0;i<n;i++){
            if(i!=cur && graph[cur][i] == 1){
                // 代表有边
                // 如果剔除i->i,则father[cur] != i
                if(visit[i] == 1 && father[i] != cur){
                    return true;
                }else{
                    if(visit[i] == 0){
                        father[i] = cur;
                        if(canFinishBase(graph, n, i, visit, father))
                            return true;
                    }
                }
            }
        }
        visit[cur] = 2; // 代表该点访问完成,下次访问的话不会是1的状态,说明应该是另一个连通分量
        return false;

    }
    bool canFinishV2(int numCourses, vector<vector<int>>& prerequisites){
        int** graph = new int*[numCourses];
        for(int i=0;i<numCourses;i++){
            graph[i] = new int[numCourses];
        }
        int n = numCourses;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                graph[i][j] = 0;
            }
        }
        for(auto v: prerequisites){
            graph[v[0]][v[1]] = 1; // first->second
        }
        int* father = new int[n];
        int* visit = new int[n];
        for(int i=0;i<n;i++){
            father[i] = -1;
            visit[i] = 0;
        }
        for(int i=0;i<n;i++)
            if(visit[i] == 0)
                if(canFinishBase(graph, n, i, visit, father)){
                    // 存在环
                    return false;
                }
        // 不存在环
        return true;

    }
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int graph[numCourses][numCourses];
        memset(graph, 0, sizeof(graph));
        vector<int> rudu(numCourses);
        for(auto v: prerequisites){
            graph[v[0]][v[1]] = 1; // first->second
            rudu[v[1]] += 1;
        }
        int n = numCourses;

        // adjust whether existing circle
        vector<int> nodes;
        int count = 0;
        for(int i=0;i<numCourses;i++){
            if(rudu[i] == 0){
                // 入度为0
                nodes.push_back(i);
                count += 1;
            }
        }
        while(!nodes.empty()){
            int cur_node = nodes.back();
            nodes.pop_back();
            for(int i=0;i<numCourses;i++){
                if(graph[cur_node][i] == 1){
                    graph[cur_node][i] = 0;
                    rudu[i] -= 1;
                    if(rudu[i] == 0){
                        nodes.push_back(i);
                        count += 1;
                    }
                }
            }
        }
        return count == numCourses;
    }
};
;