In this problem, a tree is an undirected graph that is connected and has no cycles.
You are given a graph that started as a tree with n nodes labeled from 1 to n, with one additional edge added. The added edge has two different vertices chosen from 1 to n, and was not an edge that already existed. The graph is represented as an array edges of length n where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the graph.
Return an edge that can be removed so that the resulting graph is a tree ofnnodes. If there are multiple answers, return the answer that occurs last in the input.
We can iterate through edges and find both vertices of the edge to see if they belong to the same segment. If so, we’ve located our redundant edge and can return it. If not, we should merge the two different segments with union.
⏰ Time complexity: O(n * α(n)), where N is number of vertices in the graph
Explanation: Using both path compression and union by size ensures that the amortized time per UnionFind operation is only α(n), which is optimal, where α(n) is the inverse Ackermann function. This function has a value α(n) < 5 for any value of n that can be written in this physical universe, so the disjoint-set operations take place in essentially constant time. Reference: https://en.wikipedia.org/wiki/Disjoint-set_data_structure
🧺 Space complexity: O(n) as we store parent and rank arrays of size n in Union-Find data structure.
Although it’s less well-known than Union Find and DFS, this problem can be effectively addressed using Tarjan’s Bridge finding algorithm. By capturing the low and disc values for each node during traversal, we can identify any bridges.
In this problem, our goal is to find the last edge that is not a bridge. I incorporated a somewhat awkward swap logic since the bridge check is conducted post-traversal, but overall, this remains a standard implementation of the Bridge finding algorithm.
classSolution {
int time = 1;
publicint[]findRedundantConnection(int[][] edges) {
int n = edges.length;
int[] low =newint[n + 1];
int[] disc =newint[n + 1];
List<List<Integer>> adjList = buildGraph(edges);
dfs(adjList, 1, low, disc, -1);
for (int i = n - 1; i >= 0; i--) {
int[] edge = edges[i];
int u = edge[0];
int v = edge[1];
// Swap since u must always be the earlier node during our checkif (disc[u]> disc[v]) {
int temp = u;
u = v;
v = temp;
}
if (low[v]> disc[u]){
// We have bridgecontinue;
} else {
return edge;
}
}
returnnull;
}
private List<List<Integer>>buildGraph(int[][] edges) {
int n = edges.length;
List<List<Integer>> adjList =new ArrayList<>();
for (int i = 0; i <= n; i++) {
adjList.add(new ArrayList<>());
}
for (int[] edge: edges) {
int src = edge[0];
int dst = edge[1];
adjList.get(src).add(dst);
adjList.get(dst).add(src);
}
return adjList;
}
privatevoiddfs(List<List<Integer>> adjList, int u, int[] low, int[] disc, int parent) {
if (disc[u]!= 0) {
return;
}
disc[u]= time;
low[u]= time;
time++;
for (int v: adjList.get(u)) {
if (v == parent) {
continue;
}
if (disc[v]== 0) {
dfs(adjList, v, low, disc, u);
low[u]= Math.min(low[u], low[v]);
} else {
low[u]= Math.min(low[u], disc[v]);
}
}
}
}