代码随想录算法训练营第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;
    }
}
相关推荐
闻缺陷则喜何志丹6 分钟前
【C++动态规划】1105. 填充书架|2104
c++·算法·动态规划·力扣·高度·最小·书架
AI人H哥会Java8 分钟前
【Spring】基于XML的Spring容器配置——<bean>标签与属性解析
java·开发语言·spring boot·后端·架构
Dong雨17 分钟前
六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序
数据结构·算法·排序算法
开心工作室_kaic18 分钟前
springboot493基于java的美食信息推荐系统的设计与实现(论文+源码)_kaic
java·开发语言·美食
缺少动力的火车20 分钟前
Java前端基础—HTML
java·前端·html
达帮主26 分钟前
7.C语言 宏(Macro) 宏定义,宏函数
linux·c语言·算法
loop lee28 分钟前
Redis - Token & JWT 概念解析及双token实现分布式session存储实战
java·redis
ThetaarSofVenice29 分钟前
能省一点是一点 - 享元模式(Flyweight Pattern)
java·设计模式·享元模式
InSighT__30 分钟前
设计模式与游戏完美开发(2)
java·游戏·设计模式
神仙别闹31 分钟前
基于Java2D和Java3D实现的(GUI)图形编辑系统
java·开发语言·3d