Tarjan(割点 & 割边 & 点双 & 边双)& Kosaraju & 欧拉路 笔记
迟到整整一年的笔记。
Tarjan
Tarjan 就是一种比较高级的 DFS,它按照深搜遍历的顺序打上时间戳,并维护一个
void dfs(int u, int fa) {
dfn[u] = low[u] = ++idx;
for (int i = head[u]; i; i = edge[i].next) {
int v = edge[i].v;
if (!dfn[v]) {
dfs(v, u);
low[u] = min(low[u], low[v]); //(1)
} else if (dfn[v] < dfn[u] and v != fa)
low[u] = min(low[u], dfn[v]); //(2)
}
}很好理解,(1)处的出边
割点
定义:一张无向图中,删去某个点整个图就不连通,这个点就是割点。
如果有一个点
对于根节点,如果它有超过
割边
定义:一张无向图中,删去某条边整个图就不连通,这条边就是割边。
和割点差不多,不过
而且没有根节点的判断了。
点双连通分量
定义:一张无向图的某张连通的子图不存在割点,简称点双。
如果出现了割点,只是删去割点后,割点上方和(割点及割点下方)无法连通而已,所以割点及割点下方就是一个点双,将这个点双“删去”,接着做。
“删去”的操作用栈很好维护,假设一开始是空栈,每个点遍历到了就丢进栈里,如果遇到
边双连通分量
定义:一张无向图的某张连通的子图不存在割边,简称边双。
这个更简单了,不用像点双一样担心那个割点到底算进哪个点双,直接把割边全部求出来再跑一遍别走割边就好了。
Kosaraju
求 SCC(强连通分量)的。
SCC 定义:一张有向图的某张连通的子图中的点可以互相到达。
这个最简单了,还好记,建正图跑 dfs 倒序全部进栈,接着从栈顶开始取,取出来的点在反图上跑,能跑到的地方而且还没被别的 SCC 占领的就是同一个 SCC,证明我不会。
void dfs(int u) {
if (vis[u])
return;
vis[u] = true;
for (auto v : g[u])
dfs(v);
st.push(u);
}
void dfs2(int u) {
if (scc[u])
return;
scc[u] = scccnt;
sz[scccnt]++;
for (auto v : rg[u])
dfs2(v);
}
void kosaraju() {
for (int i = 1; i <= n; i++)
dfs(i);
while (!st.empty()) {
if (!scc[st.top()]) {
scccnt++;
dfs2(st.top());
}
st.pop();
}
}欧拉路
定义:可以不重不漏一次走完整张图的路径。
- 无向图:
- 图是连通的。
- 欧拉路:
个奇数度数顶点。 - 欧拉环:没有奇数度数顶点。
- 有向图:
- 图是 SCC。
- 欧拉路:最多有
个顶点出度比入度大且只大 ,是欧拉路的起点。最多有 个顶点入度比出度大且只大 ,是欧拉路的终点。 - 欧拉环:每个点的入度和出度相等。