代码随想录Day56 108. 冗余连接,109. 冗余连接II。

1.冗余连接

卡码网题目链接(ACM模式)(opens new window)

题目描述

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

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

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

输入描述

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

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

输出描述

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

输入示例

3
1 2
2 3
1 3

输出示例

1 3

提示信息

图中的 1 2,2 3,1 3 等三条边在删除后都能使原图变为一棵合法的树。但是 1 3 由于是标准输出里最后出现的那条边,所以输出结果为 1 3

数据范围:

1 <= N <= 1000.

思路

这道题目也是并查集基础题目。

这里我依然降调一下,并查集可以解决什么问题:两个节点是否在一个集合,也可以将两个节点添加到一个集合中。

如果还不了解并查集,可以看这里:并查集理论基础

我们再来看一下这道题目。

题目说是无向图,返回一条可以删去的边,使得结果图是一个有着N个节点的树(即:只有一个根节点)。

如果有多个答案,则返回二维数组中最后出现的边。

那么我们就可以从前向后遍历每一条边(因为优先让前面的边连上),边的两个节点如果不在同一个集合,就加入集合(即:同一个根节点)。

如图所示,节点A 和节点 B 不在同一个集合,那么就可以将两个 节点连在一起。

如果边的两个节点已经出现在同一个集合里,说明着边的两个节点已经连在一起了,再加入这条边一定就出现环了。

如图所示:

已经判断 节点A 和 节点B 在在同一个集合(同一个根),如果将 节点A 和 节点B 连在一起就一定会出现环。

这个思路清晰之后,代码就很好写了。

拓展

题目要求 “请删除标准输入中最后出现的那条边” ,不少录友疑惑,这代码分明是遇到在同一个根的两个节点立刻就返回了,怎么就求出 最后出现的那条边 了呢。

有这种疑惑的录友是 认为发现一条冗余边后,后面还可能会有一条冗余边。

其实并不会。

题目是在 树的基础上 添加一条边,所以冗余边仅仅是一条。

到这一条可能靠前出现,可能靠后出现。

例如,题目输入示例:

输入示例

3
1 2
2 3
1 3

图:

输出示例

1 3

当我们从前向后遍历,优先让前面的边连上,最后判断冗余边就是 1 3。

如果我们从后向前便利,优先让后面的边连上,最后判断的冗余边就是 1 2。

题目要求“请删除标准输入中最后出现的那条边”,所以 1 3 这条边才是我们要求的。

public class Redundant_Connection {private int[] parent;//parent 数组存储每个节点的父节点。private int[] rank;//rank 数组存储每个集合的排名,用于优化合并操作。public Redundant_Connection(int n) {parent = new int[n];rank = new int[n];for (int i = 0; i < n; i++) {parent[i] = i;rank[i] = 1;}}private int find(int x) {//find 方法用于查找节点的根节点,并进行路径压缩。if (x != parent[x]) {parent[x] = find(parent[x]);}return parent[x];}public boolean union(int x, int y) {//union 方法用于合并两个节点的集合。int rootX = find(x);int rootY = find(y);if (rootX == rootY) {return false;}if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;rank[rootX] += rank[rootY];} else {parent[rootX] = rootY;rank[rootY] += rank[rootX];}return true;}public static int[] findRedundantConnection(int[][] edges) {//接受一个边的数组,使用并查集来检测冗余连接。遍历所有边,尝试合并每条边连接的两个节点。如果两个节点已经在同一个集合中,则当前边是冗余连接。int n = edges.length + 1;Redundant_Connection rc = new Redundant_Connection(n);for (int[] edge : edges) {if (!rc.union(edge[0], edge[1])) {return edge;}}return new int[]{-1, -1};}public static void main(String[] args) {//读取节点数和边数。读取所有边的信息。调用 findRedundantConnection 方法找到冗余连接,并输出结果。Scanner scanner = new Scanner(System.in);int n = scanner.nextInt();int m = scanner.nextInt();int[][] edges = new int[m][2];for (int i = 0; i < m; i++) {edges[i][0] = scanner.nextInt();edges[i][1] = scanner.nextInt();}int[] redundantEdge = findRedundantConnection(edges);System.out.println("Redundant connection: " + Arrays.toString(redundantEdge));scanner.close();}
}
  • 时间复杂度:O(mα(n)),其中m是边数,n是节点数,α是阿克曼函数的反函数,对于所有实际输入都是一个非常小的常数
  • 空间复杂度:O(n),用于存储并查集的数据结构

2.冗余连接II

卡码网题目链接(ACM模式)(opens new window)

题目描述

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

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

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

输入描述

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

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

输出描述

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

输入示例

3
1 2
1 3
2 3

1
2
3
4

输出示例

2 3

提示信息

在删除 2 3 后有向图可以变为一棵合法的有向树,所以输出 2 3

数据范围:

1 <= N <= 1000.

思路

本题与 108.冗余连接 类似,但本题是一个有向图,有向图相对要复杂一些。

本题的本质是 :有一个有向图,是由一颗有向树 + 一条有向边组成的 (所以此时这个图就不能称之为有向树),现在让我们找到那条边 把这条边删了,让这个图恢复为有向树。

还有“若有多条边可以删除,请输出标准输入中最后出现的一条边”,这说明在两条边都可以删除的情况下,要删顺序靠后的边!

我们来想一下 有向树的性质,如果是有向树的话,只有根节点入度为0,其他节点入度都为1(因为该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点)。

所以情况一:如果我们找到入度为2的点,那么删一条指向该节点的边就行了。

如图:

找到了节点3 的入度为2,删 1 -> 3 或者 2 -> 3 。选择删顺序靠后便可。

但 入度为2 还有一种情况,情况二,只能删特定的一条边,如图:

节点3 的入度为 2,但在删除边的时候,只能删 这条边(节点1 -> 节点3),如果删这条边(节点4 -> 节点3),那么删后本图也不是有向树了(因为找不到根节点)。

综上,如果发现入度为2的节点,我们需要判断 删除哪一条边,删除后本图能成为有向树。如果是删哪个都可以,优先删顺序靠后的边。

情况三: 如果没有入度为2的点,说明 图中有环了(注意是有向环)。

如图:

对于情况三,删掉构成环的边就可以了。

写代码

把每条边记录下来,并统计节点入度

前两种入度为2的情况,一定是删除指向入度为2的节点的两条边其中的一条,如果删了一条,判断这个图是一个树,那么这条边就是答案。

同时注意要从后向前遍历,因为如果两条边删哪一条都可以成为树,就删最后那一条。

再来看情况三,明确没有入度为2的情况,那么一定有向环,找到构成环的边就是要删除的边。

可以定义一个函数。

大家应该知道了,我们要解决本题要实现两个最为关键的函数:

  • isTreeAfterRemoveEdge() 判断删一个边之后是不是有向树
  • getRemoveEdge() 确定图中一定有了有向环,那么要找到需要删除的那条边

此时就用到并查集了。

如果还不了解并查集,可以看这里:并查集理论基础(opens new window)

isTreeAfterRemoveEdge() 判断删一个边之后是不是有向树: 将所有边的两端节点分别加入并查集,遇到要 要删除的边则跳过,如果遇到即将加入并查集的边的两端节点 本来就在并查集了,说明构成了环。

如果顺利将所有边的两端节点(除了要删除的边)加入了并查集,则说明 删除该条边 还是一个有向树

getRemoveEdge()确定图中一定有了有向环,那么要找到需要删除的那条边: 将所有边的两端节点分别加入并查集,如果遇到即将加入并查集的边的两端节点 本来就在并查集了,说明构成了环。

public class Redundant_ConnectionII {static int n;// 存储图中节点的数量。static int[] father = new int[1001];//用于存储每个节点的父节点信息,初始化为自身,表示每个节点最初都是独立的集合。public static void init() {//初始化并查集,将每个节点的父节点设置为自身。for (int i = 1; i <= n; ++i) {father[i] = i;}}public static int find(int u) {//并查集的查找操作,用于找到节点u的根节点,并进行路径压缩以优化后续查找。if (u == father[u]) return u;return father[u] = find(father[u]);}public static void join(int u, int v) {//并查集的合并操作,用于合并节点u和v所在的集合。u = find(u);v = find(v);if (u != v) {father[v] = u;}}public static boolean same(int u, int v) {//检查两个节点u和v是否在同一个集合中。return find(u) == find(v);}public static void getRemoveEdge(List<int[]> edges) {//遍历所有边,使用并查集检查是否存在冗余连接。如果发现两个节点已经在同一个集合中,则输出这条边作为冗余连接并返回。init();for (int i = 0; i < n; i++) {if (same(edges.get(i)[0], edges.get(i)[1])) {System.out.println(edges.get(i)[0] + " " + edges.get(i)[1]);return;} else {join(edges.get(i)[0], edges.get(i)[1]);}}}public static boolean isTreeAfterRemoveEdge(List<int[]> edges, int deleteEdge) {//检查在移除特定边deleteEdge后,图是否仍然是一棵树(连通且无环)。如果在移除边后图仍然连通,则返回true。init();for (int i = 0; i < n; i++) {if (i == deleteEdge) continue;if (same(edges.get(i)[0], edges.get(i)[1])) {return false;}join(edges.get(i)[0], edges.get(i)[1]);}return true;}public static void main(String[] args) {//使用Scanner类从标准输入读取数据。读取节点数n和边数,以及所有边的信息。初始化一个inDegree数组来存储每个节点的入度。遍历所有边,更新入度数组,并收集所有入度为2的边的索引到vec列表中。如果vec列表不为空,说明找到了至少一条冗余连接。调用isTreeAfterRemoveEdge方法检查移除这些边后图是否仍然是一棵树,如果是,则输出这条边;否则,输出vec列表中另一条边。如果vec列表为空,说明没有找到入度为2的边,调用getRemoveEdge方法来寻找冗余连接。Scanner sc = new Scanner(System.in);List<int[]> edges = new ArrayList<>();n = sc.nextInt();int[] inDegree = new int[n + 1];for (int i = 0; i < n; i++) {int s = sc.nextInt();int t = sc.nextInt();inDegree[t]++;edges.add(new int[]{s, t});}List<Integer> vec = new ArrayList<>();for (int i = n - 1; i >= 0; i--) {if (inDegree[edges.get(i)[1]] == 2) {vec.add(i);}}if (vec.size() > 0) {if (isTreeAfterRemoveEdge(edges, vec.get(0))) {System.out.println(edges.get(vec.get(0))[0] + " " + edges.get(vec.get(0))[1]);} else {System.out.println(edges.get(vec.get(1))[0] + " " + edges.get(vec.get(1))[1]);}return;}getRemoveEdge(edges);}
}
  • 时间复杂度:O(N + Mα(N)),其中N是节点数,M是边数,α是阿克曼函数的反函数,对于所有实际输入都是一个非常小的常数。
  • 空间复杂度:O(N),用于存储并查集的数据结构。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/496533.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MySQL外键类型与应用场景总结:优缺点一目了然

前言&#xff1a; MySQL的外键简介&#xff1a;在 MySQL 中&#xff0c;外键 (Foreign Key) 用于建立和强制表之间的关联&#xff0c;确保数据的一致性和完整性。外键的作用主要是限制和维护引用完整性 (Referential Integrity)。 主要体现在引用操作发生变化时的处理方式&…

双指针——查找总价格为目标值的两个商品

一.题目描述 LCR 179. 查找总价格为目标值的两个商品 - 力扣&#xff08;LeetCode&#xff09; 二.题目解析 这个题目非常简单&#xff0c;其实就是判断有没有两个数加起来等于target。 三.算法解析 1.暴力解法 暴力解法的话我们可以枚举出所有的情况&#xff0c;然后判…

使用 HTML5 Canvas 实现动态蜈蚣动画

使用 HTML5 Canvas 实现动态蜈蚣动画 1. 项目概述 我们将通过 HTML 和 JavaScript 创建一个动态蜈蚣。蜈蚣由多个节段组成&#xff0c;每个节段看起来像一个小圆形&#xff0c;并且每个节段上都附带有“脚”。蜈蚣的头部会在画布上随机移动。 完整代码在底部&#xff01;&…

Unity2021.3.16f1可以正常打开,但是Unity2017.3.0f3却常常打开闪退或者Unity2017编辑器运行起来就闪退掉

遇到问题&#xff1a; 从今年开始&#xff0c;不知道咋回事&#xff0c;电脑上的Unity2017像是变了个人似得&#xff0c;突然特别爱闪退掉&#xff0c;有时候还次次闪退&#xff0c;真是让人无语&#xff0c;一直以来我都怀疑是不是电脑上安装了什么别的软件了&#xff0c;导致…

深度学习中的并行策略概述:2 Data Parallelism

深度学习中的并行策略概述&#xff1a;2 Data Parallelism 数据并行&#xff08;Data Parallelism&#xff09;的核心在于将模型的数据处理过程并行化。具体来说&#xff0c;面对大规模数据批次时&#xff0c;将其拆分为较小的子批次&#xff0c;并在多个计算设备上同时进行处…

如何快速找到合适的科学问题

前面已经讲过 如何快速判断学术论文质量与相关性 如何描述科学问题&#xff1f;从“术”入手&#xff0c;悟出属于自己的“道” 医学图像分割任务中的典型科学问题 如何快速肝论文&#xff1f; 博士论文的写作架构 这些内容分别阐述了 如何找到重要的相关论文 找到科学问…

如何为运行在 PICO 4 Ultra 设备上的项目设置外部文件读写权限?

PICO 4 Ultra 系列设备使用的安卓操作系统为 Android 14。当项目的 Write Permission 为 Externa (SDCard) 且 Android API Level 大于 32 时&#xff0c;Unity 提供的外部文件读取方式在 PICO 4 Ultra 设备上将失效。此问题提供两种解决方法&#xff0c;按实际情况选取。 解决…

MacOS安装Xcode(非App Store)

文章目录 访问官网资源页面 访问官网资源页面 直接访问官网的历史版本下载资源页面地址&#xff1a;https://developer.apple.com/download/more/完成APP ID的登陆&#xff0c;直接找到需要的软件下载即可 解压后&#xff0c;安装将xcode.app移动到应用程序文件夹。

OpenLinkSaas使用手册-Git工具

在OpenLinkSaas的工具箱里面&#xff0c;最基础的一个就是Git仓库管理。Git仓库功能让git使用更加简单和强大&#xff0c;不仅可以使用常规的commit/pull/push/branch等功能外&#xff0c;还连接了Git仓库供应商的能力。 OpenLinkSass支持使用国内主流的Git仓库供应商的账号登录…

.NET平台用C#通过字节流动态操作Excel文件

在.NET开发中&#xff0c;通过字节流动态操作Excel文件提供了一种高效且灵活的方式处理数据。这种方法允许开发者直接在内存中创建、修改和保存Excel文档&#xff0c;无需依赖直接的文件储存、读取操作&#xff0c;从而提高了程序的性能和安全性。使用流技术处理Excel不仅简化了…

vue之axios基本使用

文章目录 1. axios 网络请求库2. axiosvue 1. axios 网络请求库 <body> <input type"button" value"get请求" class"get"> <input type"button" value"post请求" class"post"> <!-- 官网提供…

STM32开发笔记123:使用FlyMcu下载程序

文章目录 前言一、FlyMcu二、电路图三、使用方法1、配置2、读取器件信息3、擦除芯片4、加载文件下载程序5、启动应用程序前言 本文介绍使用FlyMcu下载程序到STM32微控制器的方法。 一、FlyMcu FlyMcu轻量级,比STM32CubeProgrammer使用更为简便,下载地址:http://www.mcuis…

mysql返回N/A

在写统计图的接口&#xff0c;sql查询一直无数据&#xff0c;给的默认值也没有实现&#xff1a; SELECTifnull( unit.num, 0 ) riskUnitCount,ifnull( EVENT.num, 0 ) riskEventCount,ifnull( measure.num, 0 ) riskMeasureCount FROMtb_companyLEFT JOIN (SELECTrisk.qyid,co…

Linux网络——TCP的运用

系列文章目录 文章目录 系列文章目录一、服务端实现1.1 创建套接字socket1.2 指定网络接口并bind2.3 设置监听状态listen2.4 获取新链接accept2.5 接收数据并处理&#xff08;服务&#xff09;2.6 整体代码 二、客户端实现2.1 创建套接字socket2.2 指定网络接口2.3 发起链接con…

C/C++ 数据结构与算法【哈夫曼树】 哈夫曼树详细解析【日常学习,考研必备】带图+详细代码

哈夫曼树&#xff08;最优二叉树&#xff09; 1&#xff09;基础概念 **路径&#xff1a;**从树中一个结点到另一个结点之间的分支构成这两个结点间的路径。 **结点的路径长度&#xff1a;**两结点间路径上的分支数。 **树的路径长度&#xff1a;**从树根到每一个结点的路径…

Nginx的性能分析与调优简介

Nginx的性能分析与调优简介 一、Nginx的用途二、Nginx负载均衡策略介绍与调优三、其他调优方式简介四、Nginx的性能监控 一、Nginx的用途 ‌Nginx是一种高性能的HTTP和反向代理服务器&#xff0c;最初作为HTTP服务器开发&#xff0c;主要用于服务静态内容如HTML文件、图像、视…

uniapp使用live-pusher实现模拟人脸识别效果

需求&#xff1a; 1、前端实现模拟用户人脸识别&#xff0c;识别成功后抓取视频流或认证的一张静态图给服务端。 2、服务端调用第三方活体认证接口&#xff0c;验证前端传递的人脸是否存在&#xff0c;把认证结果反馈给前端。 3、前端根据服务端返回的状态&#xff0c;显示在…

基于SpringBoot的“房产销售平台”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“房产销售平台”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统整体模块图 登录窗口界面 房源信息管理窗口界…

EMC——射频场感应的传导骚扰抗扰度(CS)

术语和定义 AE&#xff08;辅助设备&#xff09; 为受试设备正常运行提供所需信号的设备和检验受试设备性能的设备&#xff1b; 钳注入 是用电缆上的钳合式“电流”注入装置获得的钳注入&#xff1b; 电流钳 由被注入信号的电缆构成的二次绕组实现的电流变换器&#xff1b; 电磁…

探究音频丢字位置和丢字时间对pesq分数的影响

丢字的本质 丢字的本质是在一段音频中一小段数据变为0 丢字对主观感受的影响 1. 丢字位置 丢字的位置对感知效果有很大影响。如果丢字发生在音频信号的静音部分或低能量部分&#xff0c;感知可能不明显&#xff1b;而如果丢字发生在高能量部分或关键音素上&#xff0c;感知…