Find Edges Removing which does not Disconnect the Graph


Analyse the connectedness of each edge in the Graph to locate edges whose removal won't break the Graph. We can identify which edges are essential for preserving connectivity between nodes by methodically examining the effect of eliminating individual edges. "Bridge edges" or "critical edges" are edges that, when eliminated, nonetheless leave the Graph connected. These edges are essential for maintaining the general structure of the Graph and avoiding disconnection. To ensure system robustness and effective communication, such edges must be identified in network analysis, transportation planning, and infrastructure design.

Methods Used

  • Tarjan's Algorithm

  • Kruskal's Algorithm

Tarjan's Algorithm

In order to locate essential edges in a graph and discover strongly connected components, Tarjan's Algorithm is utilised. It assigns distinctive identities (low−link values) to each node based on the order of traversal by conducting a depth−first search. When a node's low−link value and its ancestor's identity match, a highly connected component has been found. Nodes within the same component are connected by non−critical edges, which means they can be removed without disconnecting the Graph. However, to preserve overall connection, edges linking nodes from various components are essential and must be kept. In order to efficiently analyse the graph's structure and identify critical edges, Tarjan's algorithm is used.

Algorithm

  • Set up an empty stack to record visited nodes, an empty list to store critical edges, and a counter to track node discovery order.

  • Start the depth−first search (DFS) exploration from any point in the Graph.

  • Based on the order of discovery, assign distinct low−link values to each node during the DFS and store them on the stack.

  • When visiting a node, update its low−link value to the minimum of its own index and the low−link values of its neighbouring nodes in order to spot highly linked components (SCCs). The node is the root of a strongly connected component if the node's low−link value coincides with its index. Add nodes to the component list by popping them from the stack up till the root.

  • Continue the DFS exploration, discovering and storing all the strongly related components in the Graph. Repeat the SCC identification.

  • nvestigate the neighbours of each node in a tightly connected component.

  • If a neighbouring node is a member of another SCC, the edge that connects them is critical.

  • Add critical edges to the list for further examination in order to store them.

  • Keep going through the Graph until each node has been seen and dealt with.

  • The list of critical edges acquired in step 8 is comprised of the edges that, if removed, would cause the graph to become disconnected. For overall connectivity to be maintained, these edges are crucial. Program identifies critical edges for graph integrity and structure analysis.

Example

#include <iostream>
#include <vector>
using namespace std;

const int MAX_NODES = 1000;

vector<int> adj[MAX_NODES];
vector<bool> visited;
vector<int> disc, low;
vector<pair<int, int>> criticalEdges;

int timeCounter = 0;

void dfs(int u, int parent) {
    visited[u] = true;
    disc[u] = low[u] = ++timeCounter;

    for (int v : adj[u]) {
        if (v == parent) continue;

        if (!visited[v]) {
            dfs(v, u);
            low[u] = min(low[u], low[v]);
            if (low[v] > disc[u])
                criticalEdges.emplace_back(min(u, v), max(u, v));
        } else {
            low[u] = min(low[u], disc[v]);
        }
    }
}

void findCriticalEdges(int nodes) {
    visited.assign(nodes, false);
    disc.assign(nodes, 0);
    low.assign(nodes, 0);
    criticalEdges.clear();
    timeCounter = 0;

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

int main() {
    int nodes = 5; // Number of nodes in the graph

    // Add edges to the adjacency list
    adj[0].push_back(1);
    adj[1].push_back(0);
    adj[1].push_back(2);
    adj[2].push_back(1);
    adj[1].push_back(3);
    adj[3].push_back(1);
    adj[3].push_back(4);
    adj[4].push_back(3);

    findCriticalEdges(nodes);

    cout << "Critical edges in the Graph:\n";
    for (const auto& edge : criticalEdges) {
        cout << edge.first << " " << edge.second << "\n";
    }

    return 0;
}

Output

Critical edges in the Graph:
1 2
3 4
1 3
0 1

Kruskal's Algorithm

A minimum spanning tree (MST) is created using Kruskal's Algorithm in the context of locating edges whose removal does not disconnect the graph. The algorithm iteratively adds the edges with the least weights to the MST while making sure no cycles arise, sorting the edges in ascending order of their weights. The edges necessary for preserving connectivity inside the Graph are those that are included in the MST. The remaining edges, those excluded from the MST, can be regarded as non−critical edges because their removal wouldn't cause the Graph to be disconnected but might make it heavier and less effective.

Algorithm

  • Create an empty list called "non_critical_edges" to contain the non−critical edges.

  • G's edges should be sorted according to increasing weights.

  • Create a disjoint set data structure and initialise it to track connected elements.

  • One disjoint set should be created for each vertex in G.

  • In the sorted edge list, for each edge (u, v),

    Verify whether the MST produces a cycle when the edge (u, v) is added:

    • Locate the root (representative) of the sets containing the vertices u and v using the disjoint set data structure.

    • There is no cycle if the roots are different; to the MST, add the edge (u, v). If not, skip this edge and move on to the next.

    The MST is finished if there are (n − 1) edges left after processing all of the edges. Break the cycle.

  • The Graph is not fully linked and there are non−critical edges if the number of edges in the MST is fewer than (n − 1).

  • Add the remaining edges (not found in the MST) to the "non_critical_edges" list by iterating over the sorted edge list once more.

  • The edges whose removal wouldn't cause the Graph to disconnect are now contained in "non_critical_edges".

Example

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct CustomEdge {
    int u, v, weight;
};

struct UniqueDisjointSet {
    vector<int> parent, rank;

    UniqueDisjointSet(int n) {
        parent.resize(n);
        rank.resize(n, 0);
        for (int i = 0; i < n; ++i)
            parent[i] = i;
    }

    int find(int u) {
        if (u != parent[u])
            parent[u] = find(parent[u]);
        return parent[u];
    }

    void union_sets(int u, int v) {
        int root_u = find(u);
        int root_v = find(v);

        if (root_u != root_v) {
            if (rank[root_u] < rank[root_v])
                parent[root_u] = root_v;
            else if (rank[root_u] > rank[root_v])
                parent[root_v] = root_u;
            else {
                parent[root_v] = root_u;
                rank[root_u]++;
            }
        }
    }
};

vector<CustomEdge> findUniqueNonCriticalEdges(int n, vector<CustomEdge>& edges) {
    vector<CustomEdge> non_critical_edges;
    UniqueDisjointSet ds(n);
    sort(edges.begin(), edges.end(), [](const CustomEdge& a, const CustomEdge& b) {
        return a.weight < b.weight;
    });

    for (const auto& edge : edges) {
        int u = edge.u;
        int v = edge.v;

        if (ds.find(u) != ds.find(v)) {
            ds.union_sets(u, v);
        }
        else {
            non_critical_edges.push_back(edge);
        }

        if (non_critical_edges.size() == n - 1)
            break;
    }

    return non_critical_edges;
}

int main() {
    int n = 5;
    vector<CustomEdge> edges = {
        {0, 1, 2},
        {0, 2, 4},
        {1, 2, 1},
        {1, 3, 3},
        {2, 3, 5},
        {2, 4, 7},
        {3, 4, 6}
    };

    vector<CustomEdge> unique_non_critical_edges = findUniqueNonCriticalEdges(n, edges);

    cout << "Unique Non-Critical Edges:\n";
    for (const auto& edge : unique_non_critical_edges) {
        cout << "(" << edge.u << ", " << edge.v << ", " << edge.weight << ")\n";
    }

    return 0;
}

Output

Unique Non-Critical Edges:
(0, 2, 4)
(2, 3, 5)
(2, 4, 7)

Conclusion

By skillfully employing Tarjan's and Kruskal's Algorithms, we have effectively identified removable edges without disconnecting the graph. These non−critical edges are essential for preserving the general structure of the Graph and avoiding disconnection. While Kruskal's Algorithm creates a minimum spanning tree and identifies non−critical edges, Tarjan's Algorithm aids in the discovery of strongly related components. Analysis of edge connectivity helps identify the bare minimum of edges that must be deleted in order to disconnect the Graph. To maintain robustness and effective communication, it is essential to understand these important and non−critical edges in a variety of domains, including network analysis, transportation planning, and infrastructure design.

Updated on: 02-Aug-2023

85 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements