@@ -250,105 +250,131 @@ int main() {
250250## 其他语言版本
251251
252252### Java
253- ``` java
254- import java.util.ArrayList ;
255- import java.util.List ;
256- import java.util.Scanner ;
257253
254+ ``` java
255+ import java.util.* ;
256+
257+ /*
258+ * 冗余连接II。主要问题是存在入度为2或者成环,也可能两个问题同时存在。
259+ * 1.判断入度为2的边
260+ * 2.判断是否成环(并查集)
261+ */
262+
258263public class Main {
259- static int n;
260- static int [] father = new int [1001 ]; // 并查集数组
264+ /**
265+ * 并查集模板
266+ */
267+ static class Disjoint {
268+
269+ private final int [] father;
261270
262- // 并查集初始化
263- public static void init () {
264- for (int i = 1 ; i <= n; ++ i) {
265- father[i] = i;
271+ public Disjoint (int n ) {
272+ father = new int [n];
273+ for (int i = 0 ; i < n; i++ ) {
274+ father[i] = i;
275+ }
276+ }
277+
278+ public void join (int n , int m ) {
279+ n = find(n);
280+ m = find(m);
281+ if (n == m) return ;
282+ father[n] = m;
283+ }
284+
285+ public int find (int n ) {
286+ return father[n] == n ? n : (father[n] = find(father[n]));
266287 }
267- }
268288
269- // 并查集里寻根的过程
270- public static int find (int u ) {
271- if (u == father[u]) return u;
272- return father[u] = find(father[u]); // 路径压缩
289+ public boolean isSame (int n , int m ) {
290+ return find(n) == find(m);
291+ }
273292 }
274293
275- // 将 v->u 这条边加入并查集
276- public static void join (int u , int v ) {
277- u = find(u);
278- v = find(v);
279- if (u != v) {
280- father[v] = u; // 合并两棵树
294+ static class Edge {
295+ int s;
296+ int t;
297+
298+ public Edge (int s , int t ) {
299+ this . s = s;
300+ this . t = t;
281301 }
282302 }
283303
284- // 判断 u 和 v 是否有同一个根
285- public static boolean same (int u , int v ) {
286- return find(u) == find(v);
304+ static class Node {
305+ int id;
306+ int in;
307+ int out;
287308 }
288309
289- // 在有向图里找到删除的那条边,使其变成树
290- public static void getRemoveEdge (List<int[]> edges ) {
291- init(); // 初始化并查集
292- for (int i = 0 ; i < n; i++ ) { // 遍历所有的边
293- if (same(edges. get(i)[0 ], edges. get(i)[1 ])) { // 如果构成有向环了,就是要删除的边
294- System . out. println(edges. get(i)[0 ] + " " + edges. get(i)[1 ]);
295- return ;
310+ public static void main (String [] args ) {
311+ Scanner scanner = new Scanner (System . in);
312+ int n = scanner. nextInt();
313+ List<Edge > edges = new ArrayList<> ();
314+ Node [] nodeMap = new Node [n + 1 ];
315+ for (int i = 1 ; i <= n; i++ ) {
316+ nodeMap[i] = new Node ();
317+ }
318+ Integer doubleIn = null ;
319+ for (int i = 0 ; i < n; i++ ) {
320+ int s = scanner. nextInt();
321+ int t = scanner. nextInt();
322+ // 记录入度
323+ nodeMap[t]. in++ ;
324+ if (! (nodeMap[t]. in < 2 )) doubleIn = t;
325+ Edge edge = new Edge (s, t);
326+ edges. add(edge);
327+ }
328+ Edge result = null ;
329+ // 存在入度为2的节点,既要消除入度为2的问题同时解除可能存在的环
330+ if (doubleIn != null ) {
331+ List<Edge > doubleInEdges = new ArrayList<> ();
332+ for (Edge edge : edges) {
333+ if (edge. t == doubleIn) doubleInEdges. add(edge);
334+ if (doubleInEdges. size() == 2 ) break ;
335+ }
336+ Edge edge = doubleInEdges. get(1 );
337+ if (isTreeWithExclude(edges, edge, nodeMap)) {
338+ result = edge;
296339 } else {
297- join(edges . get(i)[ 0 ], edges . get(i)[ 1 ] );
340+ result = doubleInEdges . get(0 );
298341 }
342+ } else {
343+ // 不存在入度为2的节点,则只需要解除环即可
344+ result = getRemoveEdge(edges, nodeMap);
299345 }
346+
347+ System . out. println(result. s + " " + result. t);
300348 }
301349
302- // 删一条边之后判断是不是树
303- public static boolean isTreeAfterRemoveEdge ( List<int[]> edges , int deleteEdge ) {
304- init(); // 初始化并查集
305- for ( int i = 0 ; i < n; i ++ ) {
306- if (i == deleteEdge) continue ;
307- if (same(edges . get(i)[ 0 ], edges . get(i)[ 1 ])) { // 如果构成有向环了,一定不是树
350+ public static boolean isTreeWithExclude ( List< Edge > edges , Edge exculdEdge , Node [] nodeMap ) {
351+ Disjoint disjoint = new Disjoint (nodeMap . length + 1 );
352+ for ( Edge edge : edges) {
353+ if (edge == exculdEdge) continue ;
354+ // 成环则不是树
355+ if (disjoint . isSame(edge . s, edge . t)) {
308356 return false ;
309357 }
310- join(edges . get(i)[ 0 ], edges . get(i)[ 1 ] );
358+ disjoint . join(edge . s, edge . t );
311359 }
312360 return true ;
313361 }
314362
315- public static void main (String [] args ) {
316- Scanner sc = new Scanner (System . in);
317- List<int[]> edges = new ArrayList<> (); // 存储所有的边
318-
319- n = sc. nextInt(); // 顶点数
320- int [] inDegree = new int [n + 1 ]; // 记录每个节点的入度
321- for (int i = 0 ; i < n; i++ ) {
322- int s = sc. nextInt(); // 边的起点
323- int t = sc. nextInt(); // 边的终点
324- inDegree[t]++ ;
325- edges. add(new int []{s, t}); // 将边加入列表
326- }
327-
328- List<Integer > vec = new ArrayList<> (); // 记录入度为2的边(如果有的话就两条边)
329- // 找入度为2的节点所对应的边,注意要倒序,因为优先删除最后出现的一条边
330- for (int i = n - 1 ; i >= 0 ; i-- ) {
331- if (inDegree[edges. get(i)[1 ]] == 2 ) {
332- vec. add(i);
333- }
334- }
363+ public static Edge getRemoveEdge (List<Edge > edges , Node [] nodeMap ) {
364+ int length = nodeMap. length;
365+ Disjoint disjoint = new Disjoint (length);
335366
336- // 情况一、情况二
337- if (vec. size() > 0 ) {
338- // vec里的边已经按照倒叙放的,所以优先删 vec.get(0) 这条边
339- if (isTreeAfterRemoveEdge(edges, vec. get(0 ))) {
340- System . out. println(edges. get(vec. get(0 ))[0 ] + " " + edges. get(vec. get(0 ))[1 ]);
341- } else {
342- System . out. println(edges. get(vec. get(1 ))[0 ] + " " + edges. get(vec. get(1 ))[1 ]);
343- }
344- return ;
367+ for (Edge edge : edges) {
368+ if (disjoint. isSame(edge. s, edge. t)) return edge;
369+ disjoint. join(edge. s, edge. t);
345370 }
346-
347- // 处理情况三:明确没有入度为2的情况,一定有有向环,找到构成环的边返回即可
348- getRemoveEdge(edges);
371+ return null ;
349372 }
373+
350374}
375+
351376```
377+
352378### Python
353379
354380``` python
0 commit comments