为什么我们不使用时间变量来查找关节点算法的子节点和父节点的最小时间

时间:2016-06-30 14:02:36

标签: algorithm recursion graph

找到关节点的一种方法是维持节点的发现时间。在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();
    }
}

3 个答案:

答案 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的定义也很清楚。

好文章:https://codeforces.com/blog/entry/71146