代码随想录算法训练营第56天|卡码网 108.冗余连接、109.冗余连接II

1. 卡码网 108.冗余连接

题目链接:https://kamacoder.com/problempage.php?pid=1181

文章链接:https://www.programmercarl.com/kamacoder/0108.冗余连接.html

题目描述

有一个图,它是一棵树,他是拥有 n 个节点(节点编号1到n)和 n - 1 条边的连通无环无向图(其实就是一个线形图),如图:

现在在这棵树上的基础上,添加一条边(依然是n个节点,但有n条边),使这个图变成了有环图,如图:

先请你找出冗余边,删除后,使该图可以重新变成一棵树。

输入描述

第一行包含一个整数 N,表示图的节点个数和边的个数。

后续 N 行,每行包含两个整数 s 和 t,表示图中 s 和 t 之间有一条边。

输出描述

输出一条可以删除的边。如果有多个答案,请删除标准输入中最后出现的那条边。

思路:

每输入一条边需要判断是否在同一个集合中,若是,则该边是冗余边,添加则成环;若否,则添加。

注意:由于题目中仅仅添加了一条边,则一旦出现就是最后的冗余边,不会出现多个冗余边的情况。

java 复制代码
import java.util.*;

public class Main {
    static int[] father;
    static int a;
    static int b;
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(); // 节点个数
        father = new int[n+1];
        init(); // 初始化
        // for (int i=0;i<n;i++) {
        //     int v = sc.nextInt();
        //     int u = sc.nextInt();
        //     if (isSame(u,v)) {
        //         a=v;
        //         b=u;
        //         continue;
        //     }
        //     join(u,v);
        // }
        // System.out.println(a + " " + b);
        
        for (int i=0;i<n;i++) {
            int v = sc.nextInt();
            int u = sc.nextInt();
            if (isSame(u,v)) {
                System.out.println(v + " " + u);
                return;
            }
            join(u,v);
        }
    }
    
    public static void init() {
        for (int i=1;i<father.length;i++) {
            father[i]=i;
        }
    }
    
    public static void join(int u,int v) {
        u = find(u);
        v = find(v);
        if (u==v) return; // 若相等,则表示已经在同一个集合里面了
        father[v] = u;
    }
    
    public static int find(int u) {
        if (u == father[u]) return u;
        return father[u] = find(father[u]);
    }
    
    public static boolean isSame(int u,int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }
}

2. 卡码网 109.冗余连接II

题目链接:https://kamacoder.com/problempage.php?pid=1182

文章链接:https://www.programmercarl.com/kamacoder/0109.冗余连接II.html

题目描述

有一种有向树,该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。有向树拥有 n 个节点和 n - 1 条边。如图:

现在有一个有向图,有向图是在有向树中的两个没有直接链接的节点中间添加一条有向边。如图:

输入一个有向图,该图由一个有着 n 个节点(节点编号 从 1 到 n),n 条边,请返回一条可以删除的边,使得删除该条边之后该有向图可以被当作一颗有向树。

输入描述

第一行输入一个整数 N,表示有向图中节点和边的个数。

后续 N 行,每行输入两个整数 s 和 t,代表这是 s 节点连接并指向 t 节点的单向边

输出描述

输出一条可以删除的边,若有多条边可以删除,请输出标准输入中最后出现的一条边。

思路:

依题目可知:有向树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。

而有向图是在有向树中的两个没有直接链接的节点中间添加一条有向边。该有向边可能形成环,也有可能不形成环,仅仅是给有向树添加一条有向边而已。

另外要注意:只添加一条边。

会出现以下几种情况:

1️⃣存在某个节点的入度为2,即有两条有向边指向该节点。如下图。

此时需要分别判断删这两个边中的一个边之后是不是有向树,若是,则返回该边;否则,返回另一个边。如果是删哪个都可以,优先删顺序靠后的边。

对于并查集:将所有边的两端节点分别加入并查集,遇到要删除的边则跳过,如果遇到即将加入并查集的边的两端节点 本来就在并查集了,说明构成了环,此时表示删除当前边后不是有向树;若最终没有构成环,则表示删除当前边后是有向树。

2️⃣不存在节点的入度为2。如下图:

此时形成环。对于并查集就是存在某个边的两端已经存在于同一集合中了。

对于并查集:将所有边的两端节点分别加入并查集,如果遇到即将加入并查集的边的两端节点 本来就在并查集了,说明构成了环。返回当前边。

java 复制代码
import java.util.*;

public class Main {
    static int[] father;
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        father = new int[n+1];
        int[][] edges = new int[n][2];
        int[] inDegree = new int[n+1];
        for (int i=0;i<n;i++) {
            int u = sc.nextInt(); // father
            int v = sc.nextInt(); // child
            edges[i][0] = u;
            edges[i][1] = v;
            inDegree[v]++;
        }
        
        List<Integer> list = new ArrayList<>();
        for (int i=edges.length-1;i>=0;i--) {
            if (inDegree[edges[i][1]] == 2) {
                list.add(i); // 收集入度为2的边的索引 依题目是两条边 list.size() == 2
            }
        }
        
        if (list.size() > 0) {
            if (isTreeAfterRemoveEdge(list.get(0),edges)) {
                System.out.println(edges[list.get(0)][0] + " " + edges[list.get(0)][1]);
                return;
            } else {
                System.out.println(edges[list.get(1)][0] + " " + edges[list.get(1)][1]);
                return;
            }
        }
        
        isCir(edges);
        
    }
    
    public static boolean isTreeAfterRemoveEdge(int index,int[][] edges) {
        init();
        for (int i=0;i<edges.length;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 isCir(int[][] edges) {
        init();
        for (int i=0;i<edges.length;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]);
            }
        }
    }
    
    public static void init() {
        for (int i=1;i<father.length;i++) {
            father[i]=i;
        }
    }
    
    public static int find(int u) {
        if (u == father[u]) return u;
        return father[u] = find(father[u]);
    }
    
    public static void join(int u,int v) {
        u = find(u);
        v = find(v);
        if (u == v) return;
        father[v] = u;
    }
    
    public static boolean isSame(int u,int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }
}
相关推荐
顾北川_野2 分钟前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
江深竹静,一苇以航4 分钟前
springboot3项目整合Mybatis-plus启动项目报错:Invalid bean definition with name ‘xxxMapper‘
java·spring boot
confiself20 分钟前
大模型系列——LLAMA-O1 复刻代码解读
java·开发语言
Wlq041525 分钟前
J2EE平台
java·java-ee
XiaoLeisj32 分钟前
【JavaEE初阶 — 多线程】Thread类的方法&线程生命周期
java·开发语言·java-ee
为什么这亚子41 分钟前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
豪宇刘1 小时前
SpringBoot+Shiro权限管理
java·spring boot·spring
1 小时前
开源竞争-数据驱动成长-11/05-大专生的思考
人工智能·笔记·学习·算法·机器学习
Elaine2023911 小时前
02多线程基础知识
java·多线程
gorgor在码农1 小时前
Redis 热key总结
java·redis·热key