Kruskal with skip and force
Time O(E² α(V)) Space O(V + E)Sort edge indices by weight once. mst(skip, force) runs Kruskal, optionally starting with a forced edge and ignoring a skipped one. Compare with the base weight for each edge.
import java.util.*;
class Solution {
private int n;
private int[][] edges;
private Integer[] order;
public List<List<Integer>> findCriticalAndPseudoCriticalEdges(int n, int[][] edges) {
this.n = n;
this.edges = edges;
order = new Integer[edges.length];
for (int i = 0; i < edges.length; i++) order[i] = i;
Arrays.sort(order, Comparator.comparingInt(i -> edges[i][2]));
int base = mst(-1, -1);
List<Integer> critical = new ArrayList<>(), pseudo = new ArrayList<>();
for (int i = 0; i < edges.length; i++) {
if (mst(i, -1) > base) critical.add(i);
else if (mst(-1, i) == base) pseudo.add(i);
}
return List.of(critical, pseudo);
}
/** MST weight skipping one edge and/or forcing one in first; MAX_VALUE if disconnected. */
private int mst(int skip, int force) {
int[] parent = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
int total = 0, used = 0;
if (force != -1) {
parent[find(parent, edges[force][0])] = find(parent, edges[force][1]);
total += edges[force][2];
used++;
}
for (int i : order) {
if (i == skip) continue;
int a = find(parent, edges[i][0]), b = find(parent, edges[i][1]);
if (a == b) continue;
parent[a] = b;
total += edges[i][2];
used++;
}
return used == n - 1 ? total : Integer.MAX_VALUE;
}
private int find(int[] parent, int x) {
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
return x;
}
}Verdict: Small limits make 2E Kruskal runs fine.