您的位置:首页 > Web前端

LeetCode 802 Find Eventual Safe States (强连通分量,Tarjan)

2018-03-19 15:02 525 查看

LeetCode 802 Find Eventual Safe States (强连通分量,Tarjan)

Description

In a directed graph, we start at some node and every turn, walk along a directed edge of the graph. If we reach a node that is terminal (that is, it has no outgoing directed edges), we stop.

Now, say our starting node is eventually safe if and only if we must eventually walk to a terminal node. More specifically, there exists a natural number
K
so that for any choice of where to walk, we must have stopped at a terminal node in less than
K
steps.

Which nodes are eventually safe? Return them as an array in sorted order.

The directed graph has
N
nodes with labels
0, 1, ..., N-1
, where
N
is the length of
graph
. The graph is given in the following form:
graph[i]
is a list of labels
j
such that
(i, j)
is a directed edge of the graph.

Example:
Input: graph = [[1,2],[2,3],[5],[0],[5],[],[]]
Output: [2,4,5,6]
Here is a diagram of the above graph.


解题思路

根据题意,Terminal Node指的是不属于任何一个大于1的强连通分量并且没有自环的点。

可以先用Tarjan算法求出所有不是Ternimal Node的点,然后对于每个点深搜,如果这个点能走向一个不是Ternimal Node的点,那这个点就“不安全”,反之安全。

代码

class Solution {
public:
void
tarjan(vector<vector<int>> &graph, vector<vector<int>> &SCCs, stack<int> &stk, int x, int &idx, int DFN[],
int LOW[],
int VST[]) {  // 从第x个点开始

DFN[x] = LOW[x] = ++idx;  // 新进点的初始化
stk.push(x);  // 进栈
VST[x] = 1;  //表示在栈里

for (auto &node : graph[x]) {
if (!DFN[node]) {  // 如果没访问过
tarjan(graph, SCCs, stk, node, idx, DFN, LOW, VST);  // 往下进行延伸,开始递归
LOW[x] = min(LOW[x], LOW[node]);  // 更新 LOW
} else if (VST[node]) {  // 如果访问过,并且还在栈里
LOW[x] = min(LOW[x], DFN[node]);  // 更新 LOW
}
}
if (LOW[x] == DFN[x]) {  // 发现x是整个强连通分量子树里的最小根
vector<int> stronglyConnectedComponent;
int top;
do {
top = stk.top();
stronglyConnectedComponent.emplace_back(top);
cout << top << ' ';
stk.pop();
VST[top] = 0;
} while (x != top);
SCCs.emplace_back(stronglyConnectedComponent);
cout << endl;
}
}

vector<vector<int>> Tarjan(vector<vector<int>> &graph) {
int idx = 0;
stack<int> stk;
auto size = graph.size();
int DFN[size], LOW[size], VST[size];  // 每个点的时间戳,0表示没有被访问过  每个点的最小子树的根 每个点是否在栈中

fill_n(DFN, size, 0);
fill_n(LOW, size, 0);
fill_n(VST, size, 0);

vector<vector<int>> stronglyConnectedComponents;

for (int i = 0; i < size; ++i) {
if (!DFN[i]) {  // 如果这个点没有被访问过
tarjan(graph, stronglyConnectedComponents, stk, i, idx, DFN, LOW, VST);
}
}

return stronglyConnectedComponents;
}

void dfs(int x, vector<vector<int>> &graph, vector<int> &isSafe) {
if(isSafe[x]!=0)
return;
isSafe[x] = 1;
for (auto &ConnectedNode:graph[x]) {
if (isSafe[ConnectedNode] == 2) {
isSafe[x] = 2;
return;
} else if (isSafe[ConnectedNode] == 0) {
dfs(ConnectedNode, graph, isSafe);
if (isSafe[ConnectedNode] == 2)
isSafe[x] = 2;
}
}
}

vector<int> eventualSafeNodes(vector<vector<int>> &graph) {
auto size = graph.size();
vector<int> ans;
vector<int> isSafe(size, 0);  // 每个点是否安全,0代表未访问,1代表安全,2代表不安全
unordered_set<int> Points;  // 所有的点都: 属于一个大小大于1的强连通分量中或者有自环

vector<vector<int>> SCCs = Tarjan(graph);  // 获得所有的强连通分量

for (auto &SCC:SCCs) {
if (SCC.size() > 1)  // 这个强连通分量的大小大于1
for (auto &Point:SCC) {
Points.insert(Point);
isSafe[Point] = 2;  // 不安全
}
else
for (auto &connectedPoint:graph[SCC[0]]) {
if (connectedPoint == SCC[0]) {  // 这个点有自环,不是题中描述的Terminal Node
Points.insert(SCC[0]);
isSafe[SCC[0]] = 2;  // 不安全
continue;
}
}
}

for (int i = 0; i < size; ++i) {
if (!isSafe[i]) {  // 如果未访问过
dfs(i, graph, isSafe);  // 深搜来确认这个点是否能走向一个强连通分量
}
if (isSafe[i] == 1) {  // 这个点安全
ans.emplace_back(i);
}
}

return ans;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  强连通分量 Tarjan