找到关节点的一种方法是维持节点的发现时间。在disc []数组中,我们保持了顶点的发现时间,而在另一个数组low []中,我们保留了其子节点的最小发现时间,这是其根节点的父节点。
我们所做的是,如果没有访问,则递归地调用所有相邻节点的函数。如果它已经被访问过,我们只需要使用低[u]和dist [v]的最小值。其中u是父亲v。 为什么这不是min(低[u],低[v])。
Here是对上述算法的解释。
// A Java program to find articulation points in an undirected graph
import java.io.*;
import java.util.*;
import java.util.LinkedList;
// This class represents an undirected graph using adjacency list
// representation
class Graph
{
private int V; // No. of vertices
// Array of lists for Adjacency List Representation
private LinkedList<Integer> adj[];
int time = 0;
static final int NIL = -1;
// Constructor
Graph(int v)
{
V = v;
adj = new LinkedList[v];
for (int i=0; i<v; ++i)
adj[i] = new LinkedList();
}
//Function to add an edge into the graph
void addEdge(int v, int w)
{
adj[v].add(w); // Add w to v's list.
adj[w].add(v); //Add v to w's list
}
// A recursive function that find articulation points using DFS
// u --> The vertex to be visited next
// visited[] --> keeps tract of visited vertices
// disc[] --> Stores discovery times of visited vertices
// parent[] --> Stores parent vertices in DFS tree
// ap[] --> Store articulation points
void APUtil(int u, boolean visited[], int disc[],
int low[], int parent[], boolean ap[])
{
// Count of children in DFS Tree
int children = 0;
// Mark the current node as visited
visited[u] = true;
// Initialize discovery time and low value
disc[u] = low[u] = ++time;
// Go through all vertices aadjacent to this
Iterator<Integer> i = adj[u].iterator();
while (i.hasNext())
{
int v = i.next(); // v is current adjacent of u
// If v is not visited yet, then make it a child of u
// in DFS tree and recur for it
if (!visited[v])
{
children++;
parent[v] = u;
APUtil(v, visited, disc, low, parent, ap);
// Check if the subtree rooted with v has a connection to
// one of the ancestors of u
low[u] = Math.min(low[u], low[v]);
// u is an articulation point in following cases
// (1) u is root of DFS tree and has two or more chilren.
if (parent[u] == NIL && children > 1)
ap[u] = true;
// (2) If u is not root and low value of one of its child
// is more than discovery value of u.
if (parent[u] != NIL && low[v] >= disc[u])
ap[u] = true;
}
// Update low value of u for parent function calls.
else if (v != parent[u])
low[u] = Math.min(low[u], disc[v]);
}
}
// The function to do DFS traversal. It uses recursive function APUtil()
void AP()
{
// Mark all the vertices as not visited
boolean visited[] = new boolean[V];
int disc[] = new int[V];
int low[] = new int[V];
int parent[] = new int[V];
boolean ap[] = new boolean[V]; // To store articulation points
// Initialize parent and visited, and ap(articulation point)
// arrays
for (int i = 0; i < V; i++)
{
parent[i] = NIL;
visited[i] = false;
ap[i] = false;
}
// Call the recursive helper function to find articulation
// points in DFS tree rooted with vertex 'i'
for (int i = 0; i < V; i++)
if (visited[i] == false)
APUtil(i, visited, disc, low, parent, ap);
// Now ap[] contains articulation points, print them
for (int i = 0; i < V; i++)
if (ap[i] == true)
System.out.print(i+" ");
}
// Driver method
public static void main(String args[])
{
// Create graphs given in above diagrams
System.out.println("Articulation points in first graph ");
Graph g1 = new Graph(5);
g1.addEdge(1, 0);
g1.addEdge(0, 2);
g1.addEdge(2, 1);
g1.addEdge(0, 3);
g1.addEdge(3, 4);
g1.AP();
System.out.println();
System.out.println("Articulation points in Second graph");
Graph g2 = new Graph(4);
g2.addEdge(0, 1);
g2.addEdge(1, 2);
g2.addEdge(2, 3);
g2.AP();
System.out.println();
System.out.println("Articulation points in Third graph ");
Graph g3 = new Graph(7);
g3.addEdge(0, 1);
g3.addEdge(1, 2);
g3.addEdge(2, 0);
g3.addEdge(1, 3);
g3.addEdge(1, 4);
g3.addEdge(1, 6);
g3.addEdge(3, 5);
g3.addEdge(4, 5);
g3.AP();
}
}
答案 0 :(得分:0)
我认为你稍微误解了访问过的顶点情况。
在无向图的DFS中,每当我们找到从顶点u到顶点v的边缘使得v已经被访问时,这意味着它是后边缘并且v是u的祖先(除非当然是v是你的父母)。
现在从给定的代码中,low [u]的动机是存储从以u为根的子树中的任何顶点可到达的最低发现时间顶点。 (即包括你)
因此,当我们发现边缘u-> v使得v是u(而不是其父节点)的祖先时,这意味着该顶点v很可能是具有最低盘的顶点。从以u为根的子树可以到达的时间。因此我们将低[u]更新为其当前值和光盘的最小值。可以从它到达的祖先顶点v的时间。
现在为什么不低[v],只是因为如果被测试为AP的顶点x低于v,则first
足以证明它不是AP,如果它高于v,然后会有一个步骤 - &gt; arr.map(&:first)
答案 1 :(得分:0)
首先要做的事情:
disc[]
:它回答了一个简单的问题,一个特定的顶点何时被发现&#34;在深度优先搜索?中,这意味着它按照dfs
中找到的顺序为顶点指定一个数字。(未更改)
low[x]
:它回答了另一个简单的问题,&#34;什么是最低级别的顶点x
可以爬到&#34;。 (每次迭代都会有变化)
back-edge
:将顶点连接到已访问过的顶点的边,该顶点不是其直接父节点
现在,在给定的代码段中,以下部分:
else if (v != parent[u])
low[u] = Math.min(low[u], disc[v]);
指的是存在后沿的情况。在遇到后沿时,我们用它可以爬到的最低级别顶点更新父级的low
值(如果它的发现时间小于父级的较低值)。
很难找到将这段代码更改为以下内容的示例
else if (v != parent[u])
low[u] = Math.min(low[u], low[v]);
会破坏算法。话虽这么说,这两段代码在语义上都有着截然不同的含义。
Math.min(low[u], low[v]);
只是指如果孩子有后边缘,那么它的直接父母也是如此,而Math.min(low[u], disc[v]);
在语义上意味着顶点的low
值是最低级别的顶点它可以爬到。
答案 2 :(得分:0)
这是专门用于找到咬合点的。当我们遍历节点U的子级的子级V时,如果low(V)== dist(U),则U将是关节点。但是,如果U属于另一个在处理U-> V之前已处理的循环,则dist(U)> low(U)。因为U可以回到其祖先。在这种情况下,如果我们使用
"org.apache.hadoop" % "hadoop-aws" % "2.8.3" % "provided",
然后在处理V时,low(V)= low(U)。返回到U的后期处理,dist [U] <= low [V]不正确(因为low [V] = low [U] < dist [U])。然后,U将不再是一个铰接点,这是有缺陷的。
请注意,这不适用于查找网桥或Trajan的SCC,因为它们都不在乎U是骑行情况的根(dist [U] == low [v])。
我个人更喜欢一直使用dist [u],因为它是一致的,并且low的定义也很清楚。