代码随想录算法训练营第六十二天 | 108. 冗余连接、109. 冗余连接II、复习

108. 冗余连接

题目链接:https://kamacoder.com/problempage.php?pid=1181
文档讲解:https://www.programmercarl.com/kamacoder/0108.%E5%86%97%E4%BD%99%E8%BF…

思路

从前向后遍历每一条边(因为优先让前面的边连上),边的两个节点如果不在同一个集合,就加入集合(即:同一个根节点)。如果边的两个节点已经出现在同一个集合里,说明着边的两个节点已经连在一起了,再加入这条边一定就出现环了。然后直接输出。

代码

import java.util.*;

class Main {
    static int[] father;
    static int n;
    
    public static void main (String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        father = new int[n + 1];
        init();
        for (int i = 0; i < n; i++) {
            int a = in.nextInt();
            int b = in.nextInt();
            if (isSame(a, b)) {
                System.out.println(a + " " + b);
                return;
            } else {
                join(a, b);
            }
        }
    }
    
    public static void init() {
        for (int i = 1; i <= n; i++) father[i] = i;
    }
    
    public static int find (int u) {
        return u == father[u] ? u : (father[u] = find(father[u]));
    }
    
    public static boolean isSame(int u, int v) {
        return find(u) == find(v);
    }
    
    public static void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return ;
        father[v] = u;
    }
}

109. 冗余连接II

题目链接:https://kamacoder.com/problempage.php?pid=1182
文档讲解:https://www.programmercarl.com/kamacoder/0109.%E5%86%97%E4%BD%99%E8%BF…

思路

有向树的性质,如果是有向树的话,只有根节点入度为0,其他节点入度都为1。

  • 情况一:如果我们找到入度为2的点,那么删一条指向该节点的边就行了。
  • 情况二:入度为2 还有一种情况,只能删特定的一条边。
  • 情况三: 如果没有入度为2的点,说明图中有环了。

用数组把每条边记录下来,并统计节点入度,如果存在入度为2的节点,则实现函数isTreeAfterRemoveEdge(),否则实现函数getRemoveEdge()

  • isTreeAfterRemoveEdge()判断删一个边之后是不是有向树: 将所有边的两端节点分别加入并查集,遇到要删除的边则跳过,如果遇到即将加入并查集的边的两端节点本来就在并查集了,说明构成了环。如果顺利将所有边的两端节点(除了要删除的边)加入了并查集,则说明删除该条边是一个有向树。
  • getRemoveEdge()确定图中一定有了有向环,那么要找到需要删除的那条边: 将所有边的两端节点分别加入并查集,如果遇到即将加入并查集的边的两端节点 本来就在并查集了,说明构成了环。

代码

import java.util.*;
class Main {
    static int[] father, inDegree, vec;
    static int[][] edges;
    static int n;
    
    public static void main (String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        inDegree = new int[n + 1];
        vec = new int[2]; // 保存edges中对应边的行数
        father = new int[n + 1];
        edges = new int[n][2];
        for (int i = 0; i < n; i++) {
            edges[i][0] = in.nextInt();
            edges[i][1] = in.nextInt();
            inDegree[edges[i][1]]++;
        }
        int j = 0;
        for (int i = n - 1; i >= 0; i--) {
            if (inDegree[edges[i][1]] == 2) {
                vec[j++] = i;
            }
        }
        if (j > 0) {
            if (isTreeAfterRemoveEdge(vec[0])) {// 如果移掉这条边后是树,则输出
                System.out.println(edges[vec[0]][0] + " " + edges[vec[0]][1]);
            } else {
                System.out.println(edges[vec[1]][0] + " " + edges[vec[1]][1]);
            }
            return;
        }
        getRemoveEdge();
    }
    
    public static void init() {
        for (int i = 1; i <= n; i++) father[i] = i;
    }
    
    public static int find(int u) {
        return u == father[u] ? u : (father[u] = find(father[u]));
    }
    
    public static boolean isSame(int u, int v) {
        return find(u) == find(v);
    }
    
    public static void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return;
        father[v] = u;
    }
    
    public static boolean isTreeAfterRemoveEdge(int index) {
        init();
        for (int i = 0; i < n; i++) {
            if (i == index) continue;
            if (isSame(edges[i][0], edges[i][1])) return false;
            else join(edges[i][0], edges[i][1]);
        }
        return true;
    }
    
    public static void getRemoveEdge() {
        init();
        for (int i = 0; i < n; i++) {
            if (isSame(edges[i][0], edges[i][1])) {
                System.out.println(edges[i][0] + " " + edges[i][1]);
                return;
            } else join(edges[i][0], edges[i][1]);
        }
    }
}

老是忘记初始化并查集。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值