图中的桥使图断开连接

时间:2019-11-17 22:08:52

标签: algorithm data-structures graph

我正在进行编程,需要找到图形的铰接点(删除所有节点会使图形断开连接的节点)

例如,我有以下链接:

示例1

[[0,1], [0,2], [1,3], [2,3], [5,6], [3,4]]

enter image description here

The answer should be [2,3,5], because removing these nodes makes the graph disconnected.

说明:

If I remove node 2 here, the graph becomes 2 parts 0,1,3,4 and 5,6
If I remove node 3 here, the graph becomes 2 parts 0,1,2,5,6 and 4
If I remove node 5 here, the graph becomes 2 parts 0,1,2,3,4 and 6

示例2:

[[1,2], [2,3], [3,4], [4,5], [6,3]]

enter image description here

The output should be: [2, 3, 4]

说明:

If I remove node 2 here, the graph becomes 2 parts 1, and 3,4,5,6
If I remove node 3 here, the graph becomes 3 parts 1,2 and 6 and 4,5
If I remove node 4 here, the graph becomes 2 parts 1,2,3,6 and 5

如何在Java程序中实现这一目标?

2 个答案:

答案 0 :(得分:2)

import static java.lang.Math.min;

import java.util.ArrayList;
import java.util.List;

public class ArticulationPointsAdjacencyList {

  private int n, id, rootNodeOutcomingEdgeCount;
  private boolean solved;
  private int[] low, ids;
  private boolean[] visited, isArticulationPoint;
  private List<List<Integer>> graph;

  public ArticulationPointsAdjacencyList(List<List<Integer>> graph, int n) {
    if (graph == null || n <= 0 || graph.size() != n) throw new IllegalArgumentException();
    this.graph = graph;
    this.n = n;
  }

  // Returns the indexes for all articulation points in the graph even if the
  // graph is not fully connected.
  public boolean[] findArticulationPoints() {
    if (solved) return isArticulationPoint;

    id = 0;
    low = new int[n]; // Low link values
    ids = new int[n]; // Nodes ids
    visited = new boolean[n];
    isArticulationPoint = new boolean[n];

    for (int i = 0; i < n; i++) {
      if (!visited[i]) {
        rootNodeOutcomingEdgeCount = 0;
        dfs(i, i, -1);
        isArticulationPoint[i] = (rootNodeOutcomingEdgeCount > 1);
      }
    }

    solved = true;
    return isArticulationPoint;
  }

  private void dfs(int root, int at, int parent) {

    if (parent == root) rootNodeOutcomingEdgeCount++;

    visited[at] = true;
    low[at] = ids[at] = id++;

    List<Integer> edges = graph.get(at);
    for (Integer to : edges) {
      if (to == parent) continue;
      if (!visited[to]) {
        dfs(root, to, at);
        low[at] = min(low[at], low[to]);
        if (ids[at] <= low[to]) {
          isArticulationPoint[at] = true;
        }
      } else {
        low[at] = min(low[at], ids[to]);
      }
    }
  }

  /* Graph helpers */

  // Initialize a graph with 'n' nodes.
  public static List<List<Integer>> createGraph(int n) {
    List<List<Integer>> graph = new ArrayList<>(n);
    for (int i = 0; i < n; i++) graph.add(new ArrayList<>());
    return graph;
  }

  // Add an undirected edge to a graph.
  public static void addEdge(List<List<Integer>> graph, int from, int to) {
    graph.get(from).add(to);
    graph.get(to).add(from);
  }

  /* Example usage: */

  public static void main(String[] args) {
    testExample2();
  }

  private static void testExample1() {
    int n = 7;
    List < List < Integer >> graph = createGraph (n);

    addEdge (graph, 0, 1);
    addEdge (graph, 0, 2);
    addEdge (graph, 1, 3);
    addEdge (graph, 2, 3);
    addEdge (graph, 2, 5);
    addEdge (graph, 5, 6);
    addEdge (graph, 3, 4);

    ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
    boolean[] isArticulationPoint = solver.findArticulationPoints();

    // Prints:
    // Node 2 is an articulation
    // Node 3 is an articulation
    // Node 5 is an articulation
    for (int i = 0; i < n; i++)
      if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
  }

  private static void testExample2() {
    int n = 7;
    List < List < Integer >> graph = createGraph (n);

    addEdge (graph, 1, 2);
    addEdge (graph, 2, 3);
    addEdge (graph, 3, 4);
    addEdge (graph, 3, 6);
    addEdge (graph, 4, 5);


    ArticulationPointsAdjacencyList solver = new ArticulationPointsAdjacencyList(graph, n);
    boolean[] isArticulationPoint = solver.findArticulationPoints();

    // Prints:
    // Node 2 is an articulation
    // Node 3 is an articulation
    // Node 4 is an articulation
    for (int i = 0; i < n; i++)
      if (isArticulationPoint[i]) System.out.printf("Node %d is an articulation\n", i);
  }
}

参考:https://github.com/williamfiset/Algorithms/blob/master/com/williamfiset/algorithms/graphtheory/ArticulationPointsAdjacencyList.java

答案 1 :(得分:1)

有多种算法可用于查找节点,因此,如果删除这些节点,则会使图形断开连接(称为铰接点)。

在这里我解释其中之一,并提供一些实现它的代码:

Tarjan算法

给定一个图,我们希望找到所有的,这样,如果从中删除了,则该图将断开连接。

第一个观察结果是,有向图中的a(弱)连通分量等于同一图中的连通分量,但是边是无向的。因此,为简单起见,我们将视为无向图。

算法描述

在该图上,我们进行了预定的深度优先搜索(DFS)访问,其中为任何节点分配2个值,我们将其称为prelowpre代表访问该节点的时刻,low代表来自的最低可达节点的时刻。

这种访问方式是这样的:

在访问的每个步骤中,prelow都被设置为pre的下一个值。然后,如果发现某个循环正在关闭,则将起始循环节点的low设置为prelow值通过DFS回溯传输给父级。

每隔两个节点的DFS完成时,使得是邻居,并且low值大于或等于pre值,则是一个连接点。
为此有一个例外:DFS生成树的根只有在有多个子代的情况下才是关节点

示例

(在图形中,P显然表示pre,L表示low

首先,将每个顶点的prelow设置为默认值(假设为-1)



我们从节点0开始,设置他的prelow



我们转到节点1并设置其prelow



我们可以转到2或3,我们决定转到2并设置他的prelow



我们可以转到4或5,我们决定转到4并设置他的prelow



我们转到3并设置他的prelow



我们看到1已被访问;这意味着这是一个周期,因此我们将3的low更新为1的pre



通过回溯,我们返回4并更新他的low



通过回溯,我们返回2并更新其low



现在我们转到5并设置他的prelow



通过回溯,我们返回到2,但是没有任何事情要做。
我们从5返回,因此他的low值是固定的,并且大于pre的值2;所以2是一个关节点



通过回溯,我们返回到1,无事可做。
我们从2返回,因此他的low值是固定的,等于pre的值1;所以1是一个关节点



通过回溯,我们返回到0,但是没有任何事情要做。
我们从1返回,因此他的low值是固定的,并且大于pre的值0;但是0是根,并且只有一个孩子;所以它不是一个关节点



因此,我们找到了答案:[1, 2]

代码

这是S. Halim和F. Halim从《竞争编程手册》中提取并由我修改的一个非常简单易懂的代码片段(C ++)。
它不太适合“实词应用”(例如,因为它使用全局变量),但由于其简洁明了,因此可以进行竞争性编程和解释。

const int UNVISITED = -1;
vector<int> dfs_low;
vector<int> dfs_pre;
int dfsNumberCounter;
int rootChildren;
vector<vector<int>> AdjList;
vector<int> articulation_vertex;

// This function is the DFS that implement Tarjan algoritm
void articulationPoint(int u) {
  dfs_low[u] = dfs_pre[u] = dfsNumberCounter++; // dfs_low[u] <= dfs_pre[u]
  for (int j = 0; j < (int)AdjList[u].size(); j++) {
    int v = AdjList[u][j];
    if (dfs_pre[v] == UNVISITED) { // a tree edge
      dfs_parent[v] = u;
      if (u == dfsRoot) rootChildren++; // special case if u is a root

      articulationPoint(v);

      if (dfs_low[v] >= dfs_pre[u]) // for articulation point
        articulation_vertex[u] = true;  // store this information first
      dfs_low[u] = min(dfs_low[u], dfs_low[v]); // update dfs_low[u]
    }
    else if (v != dfs_parent[u]) // a back edge and not direct cycle
      dfs_low[u] = min(dfs_low[u], dfs_pre[v]); // update dfs_low[u]
} }


// Some driver code
int main() {
  ...  //Init of variables and store of the graph inside AdjList is omitted
  ...  // V is the number of nodes
  dfsNumberCounter = 0;
  dfs_pre.assign(V, UNVISITED);
  dfs_low.assign(V, 0);
  dfs_parent.assign(V, 0);
  articulation_vertex.assign(V, 0);
  rootChildren = 0;
  articulationPoint(0);
  if (root_children > 1) {
    articulation_vertex[0] = false;
  }

  printf("Articulation Points:\n");
  for (int i = 0; i < V; i++)
  if (articulation_vertex[i])
    printf(" Vertex %d\n", i);
}