从今天开始复习图论专题,从拓扑排序开始,后面接并查集、最短路、迷宫等专题。图论有的题思路并不复杂,但代码基本上都比较长,比较考验熟练度和模板准备充分不充分。
拓扑排序算是图论里比较简单的,总体思路类似剥洋葱,一层一层剥掉入度/出度为0的节点。
leetcode-207 课程表
判断可不可行本质上是在图中找环。如果存在环,那么环中所有节点在任意时刻的入读都不会降至0,那么按照队列为空的循环终止条件,最终遍历到的节点数必然会少于课程总数。如果没有环则二者应相等。
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> in_degree(numCourses);
vector<vector<int>> out_degree(numCourses);
for(auto& i : prerequisites){
int a = i[0], b = i[1];
++in_degree[a];
out_degree[b].push_back(a);
}
deque<int> queue;
for(int i = 0; i < numCourses; ++i){
if(in_degree[i] == 0) queue.push_back(i);
}
int cnt = 0;
while(!queue.empty()){
++cnt;
int course = queue.front();
queue.pop_front();
vector<int> this_pre = out_degree[course];
for(int i : this_pre){
--in_degree[i];
if(in_degree[i] == 0) queue.push_back(i);
}
}
return cnt == numCourses;
}
};
leetcode-802 找到最终的安全状态
这题类似反向的拓扑排序。经典拓扑排序剥掉入度为0的节点,这题需要剥掉出度为0的节点。画一下每层剥完之后的图能看出,按照每层剥掉出度为0的节点,等价于按照bfs方式遍历到每个节点到终端节点可能的路径(同样还可以写dfs,但写法就不一样了)。
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<int> out_degree(n);
vector<int> ans;
for(int i = 0; i < n; ++i) out_degree[i] = graph[i].size();
vector<vector<int>> in_nodes(n);
for(int i = 0; i < n; ++i){
for(int out : graph[i]) in_nodes[out].push_back(i);
}
deque<int> q;
for(int i = 0; i < n; ++i){
if(out_degree[i] == 0){
q.push_back(i);
ans.push_back(i);
}
}
while(!q.empty()){
int node = q.front();
q.pop_front();
vector<int>& this_in = in_nodes[node];
for(int in : this_in){
--out_degree[in];
if(out_degree[in] == 0){
q.push_back(in);
ans.push_back(in);
}
}
}
sort(ans.begin(), ans.end());
return ans;
}
};