题目
你这个学期必须选修 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 。这是不可能的。
分析
这是一个典型的拓扑排序问题,判断有向图中是否存在环。如果存在环,那么就无法完成所有课程的学习,因为会有课程永远无法满足先修条件。可以使用深度优先搜索(DFS)结合状态标记来解决这个问题。
DFS
代码解释
状态定义:使用枚举类型 State
定义了三种状态:UNVISITED
(未访问)、VISITING
(正在访问)、VISITED
(已访问)。
深度优先搜索函数 dfs
:course
表示当前正在访问的课程,adjList
是邻接表,存储了每门课程的后继课程,states
记录了每门课程的状态。如果当前课程正在访问,说明存在环,返回 false;
如果当前课程已访问,直接返回 true。
标记当前课程为正在访问,然后遍历当前课程的所有后继课程,对每个后继课程递归调用 dfs
,如果有任何一个后继课程无法完成,返回 false
。否则标记当前课程为已访问,返回 true
。
主函数 canFinish
:构建邻接表,将先修课程关系转化为邻接表的形式,初始化每门课程的状态为 UNVISITED
。对每门课程进行深度优先搜索,如果有任何一门课程无法完成,返回 false
。如果所有课程都能完成,返回 true
。
时间复杂度:O(),
是课程数,
为先修课程数
空间复杂度:O()
class Solution {
private:
// 定义状态:0 表示未访问,1 表示正在访问,2 表示已访问
enum class State {
UNVISITED,
VISITING,
VISITED
};
// 深度优先搜索函数
bool dfs(int course, const vector<vector<int>>& adjList, vector<State>& states) {
// 如果当前课程正在访问,说明存在环
if (states[course] == State::VISITING) {
return false;
}
// 如果当前课程已访问,直接返回 true
if (states[course] == State::VISITED) {
return true;
}
// 标记当前课程为正在访问
states[course] = State::VISITING;
// 遍历当前课程的所有后继课程
for (int nextCourse : adjList[course]) {
// 如果后继课程无法完成,说明存在环
if (!dfs(nextCourse, adjList, states)) {
return false;
}
}
// 标记当前课程为已访问
states[course] = State::VISITED;
return true;
}
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
// 构建邻接表
vector<vector<int>> adjList(numCourses);
for (const auto& prerequisite : prerequisites) {
adjList[prerequisite[1]].push_back(prerequisite[0]);
}
// 记录每个课程的状态
vector<State> states(numCourses, State::UNVISITED);
// 对每门课程进行深度优先搜索
for (int i = 0; i < numCourses; ++i) {
if (!dfs(i, adjList, states)) {
return false;
}
}
return true;
}
};
知识充电
enum class
在C++中,enum class
(强类型枚举)通过enum class
关键字定义,相比传统枚举(enum
)提供了更严格的作用域控制和类型安全性。
语法
enum class EnumName {
Value1,
Value2,
Value3
// 可以定义更多枚举值
};
作用域限定:枚举成员必须通过 EnumName::Value1
形式访问,避免全局命名冲突
显式底层类型:通过 :类型
指定底层存储类型(如int
、char
),默认是int
enum class SmallEnum : char {
A,
B,
C
};
类型安全:不同的 enum class
类型之间不能隐式转换,也不能与整数类型进行隐式转换
#include <iostream>
enum class Status { SUCCESS, FAILURE };
enum class Result { PASS, FAIL };
int main() {
Status s = Status::SUCCESS;
// 下面代码会编译错误,不能将 Status 类型赋值给 Result 类型
// Result r = s;
// 下面代码会编译错误,不能将整数隐式转换为 Status 类型
// Status wrong = 1;
return 0;
}