代码随想录算法训练营第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;
    }
}
相关推荐
serve the people3 分钟前
springboot 单独新建一个文件实时写数据,当文件大于100M时按照日期时间做文件名进行归档
java·spring boot·后端
qmx_071 小时前
HTB-Jerry(tomcat war文件、msfvenom)
java·web安全·网络安全·tomcat
为风而战1 小时前
IIS+Ngnix+Tomcat 部署网站 用IIS实现反向代理
java·tomcat
limingade2 小时前
手机实时提取SIM卡打电话的信令和声音-新的篇章(一、可行的方案探讨)
物联网·算法·智能手机·数据分析·信息与通信
技术无疆3 小时前
快速开发与维护:探索 AndroidAnnotations
android·java·android studio·android-studio·androidx·代码注入
jiao000014 小时前
数据结构——队列
c语言·数据结构·算法
迷迭所归处6 小时前
C++ —— 关于vector
开发语言·c++·算法
架构文摘JGWZ6 小时前
Java 23 的12 个新特性!!
java·开发语言·学习
leon6256 小时前
优化算法(一)—遗传算法(Genetic Algorithm)附MATLAB程序
开发语言·算法·matlab
CV工程师小林6 小时前
【算法】BFS 系列之边权为 1 的最短路问题
数据结构·c++·算法·leetcode·宽度优先