当前位置: 首页 > article >正文

Day62_补20250210_图论part6_108冗余连接|109.冗余连接II

Day62_20250210_图论part6_108冗余连接|109.冗余连接II

108冗余连接 【把题意转化为并查集问题】

题目

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

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

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

输入示例
3
1 2
2 3
1 3
输出示例
1 3

思路

  • 思路
    • 题意转化为并查集
      • 当存在两个顶点处于同一个集合时,重复2次,说明有冗余边,删除最后一个。
      • 代码,初始化DisJoint,用join将两个顶点建立一个集合,然后用isSame判断两个不在1个集合中的顶点是否在一个集合中,并且判断两个在1个集合中的顶点是否重复加入了
  • 代码
    import java.util.*;
    public class Main{public static void main(String[] args){//输入Scanner scanner=new Scanner(System.in);int n=scanner.nextInt();DisJoint disJoint=new DisJoint(n+1);int[][] edges=new int[n][2];//m条边for(int i=0;i<n;i++){edges[i][0]=scanner.nextInt();edges[i][1]=scanner.nextInt();}for(int i=0;i<n;i++){int u=edges[i][0];int v=edges[i][1];//先检查是否冗余:如果2个节点在同一集合中,【本身已经连通,才会输出冗余边】if(disJoint.isSame(u,v)){System.out.println(u+" "+v);return;}//不冗余的话将2个边加入到同一集合中disJoint.join(u,v);}}
    }
    class DisJoint{private int[] father;//父节点//初始化public DisJoint(int N){father=new int[N];for(int i=0;i<N;i++){father[i]=i;}}//寻找根节点public int find(int n){return n==father[n]?n:(father[n]=find(father[n]));}//将2个节点加入到同一个集合中public void join(int n,int m){n=find(n);m=find(m);if(n==m) return;father[m]=n;}//判断2个节点是否在同一个集合中public boolean isSame(int n,int m){return find(n)==find(m);}
    }

总结

  • 注意,题目中冗余的边的个数仅为1条。
  • 思路
    • 将2

109.冗余连接II 【难度上升】

题目

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

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

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

输入描述

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

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

输出描述

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

输入示例
3
1 2
1 3
2 3
输出示例
2 3
提示信息

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

数据范围:

1 <= N <= 1000.

思路

  • 思路
    • 与上一题的区别是,有向图
      • 有向树,根节点入度为0,其他节点入度都为1。
    • 核心:如果我们找到入度为2的点,那么删一条指向该节点的边就行了。
    • 注意特殊情况:
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 节点3的入度为2,但在删除边的时候,只能删(1到3),如果删(4-3),就不是有向树了(找不到根节点)。
        • 如果发现入度为2的节点,判断删除哪一条边,才能成为有向树,如果是删哪个都可以,优先删除顺序靠后的边。
      • 如果没有入度为2的点,说明图中有环了(有向环)。
        • 删除构成环的边就可以了。
  • 伪代码
    • 将每条边记录下来,并统计入度。
    • 情况1和情况2,从后向前遍历,如果有入度为2的情况,删除一条边后判断这个图是一个图,那么这条边是答案。如果删除哪条边都可以成为树,就删除最后那一条。
    • 如果没有入度为2的情况,一定有有向环,找到构成环的边就是要删除的边
  • 代码
    import java.util.*;public class Main {//并查集Disjoint类static class Disjoint {private int[] father;public Disjoint(int n) {father = new int[n + 1];for (int i = 1; i <= n; i++) {father[i] = i;}}public int find(int n) {if (father[n] != n) {father[n] = find(father[n]); // 路径压缩}return father[n];}public void join(int n, int m) {father[find(n)] = find(m);}public boolean isSame(int n, int m) {return find(n) == find(m);}}public static void main(String[] args) {//输入Scanner scanner = new Scanner(System.in);//scannerint n = scanner.nextInt();int[][] edges = new int[n][2]; //边int[] inDegree = new int[n + 1]; //入度Integer doubleInNode = null;//存储入度为2的节点// 读取输入并统计入度for (int i = 0; i < n; i++) {edges[i][0] = scanner.nextInt();edges[i][1] = scanner.nextInt();inDegree[edges[i][1]]++;//如果度为2,记录该节点if (inDegree[edges[i][1]] == 2) doubleInNode = edges[i][1]; }//case1:存在入度为2的节点//找到2条指向doubleNode的边,存入candidates[][]if (doubleInNode != null) {int[][] candidates = new int[2][2];int index = 0;for (int i = 0; i < n; i++) {if (edges[i][1] == doubleInNode) {candidates[index++] = edges[i];if (index == 2) break;}}// 先尝试删除第2条边(candidates[1]),如果删除后形成树,输出candidates[1],否则输出candidates[0]if (isTreeAfterRemoving(edges, candidates[1], n)) {System.out.println(candidates[1][0] + " " + candidates[1][1]);} else {System.out.println(candidates[0][0] + " " + candidates[0][1]);}return;}//case2:不存在度为2(环),找出最后一条导致环的边int[] lastEdge = getLastEdgeToRemove(edges, n);System.out.println(lastEdge[0] + " " + lastEdge[1]);}// 方法 1:删除某条边后是否形成树private static boolean isTreeAfterRemoving(int[][] edges, int[] excludeEdge, int n) {Disjoint disjoint = new Disjoint(n);//检查环for (int i = 0; i < n; i++) {// **优化点:直接比较两个值,而不是 Arrays.equals**//if当前边是要删除的边(2个点),继续(continue);if (edges[i][0] == excludeEdge[0] && edges[i][1] == excludeEdge[1]) continue;//检查是否形成环//如果起点和终点已经在同一个集合,说明形成了环if (disjoint.isSame(edges[i][0], edges[i][1])) {return false; }//合并2个节点disjoint.join(edges[i][0], edges[i][1]);}return true;}// 方法 2:找到最后出现的环边//在有向图中找到最后一条导致环的边,并返回该边private static int[] getLastEdgeToRemove(int[][] edges, int n) {Disjoint disjoint = new Disjoint(n);int[] lastEdge = null;//记录最后一条导致环的边for (int i = 0; i < n; i++) {//检查是否形成环//如果起点和终点已经在同一集合,形成了环if (disjoint.isSame(edges[i][0], edges[i][1])) {lastEdge = edges[i]; //更新存储的边} else {//合并到1个集合中disjoint.join(edges[i][0], edges[i][1]);}}return lastEdge;}
    }

总结

  • 这题好难,难在怎么写2个调用方法的代码

相关文章:

Day62_补20250210_图论part6_108冗余连接|109.冗余连接II

Day62_20250210_图论part6_108冗余连接|109.冗余连接II 108冗余连接 【把题意转化为并查集问题】 题目 有一个图&#xff0c;它是一棵树&#xff0c;他是拥有 n 个节点&#xff08;节点编号1到n&#xff09;和 n - 1 条边的连通无环无向图&#xff08;其实就是一个线形图&am…...

kafka消费端之消费者协调器和组协调器

文章目录 概述回顾历史老版本获取消费者变更老版本存在的问题 消费者协调器和组协调器新版如何解决老版本问题再均衡过程**第一阶段CFIND COORDINATOR****第二阶段&#xff08;JOINGROUP&#xff09;**选举消费组的lcader选举分区分配策略 第三阶段&#xff08;SYNC GROUP&…...

语法备忘04:将 事件处理函数 绑定到 组件 的事件上

示例1&#xff1a;<Table OnQueryAsync"OnQueryAsync" /> 示例2&#xff1a;<Table OnQueryAsync"OnQueryAsync" /> 说明&#xff1a;这两种写法在功能上是‌完全相同的‌&#xff0c;都是在将 OnQueryAsync 事件处理函数绑定到 Table 组件的 …...

C++20中的std::atomic_ref

一、std::atomic_ref 我们在学习C11后的原子操作时&#xff0c;都需要提前定义好std::atomic变量&#xff0c;然后才可以在后续的应用程序中进行使用。原子操作的优势在很多场合下优势非常明显&#xff0c;所以这也使得很多开发者越来习惯使用原子变量。 但是&#xff0c;在实…...

CSS 相关知识

1、高度已知&#xff0c;三栏布局&#xff0c;左右宽度 200&#xff0c;中间自适应&#xff0c;如何实现&#xff1f; <body><div class"box"><div class"box1">高度已知</div><div class"box2">左右宽度 200&…...

RocketMQ、RabbitMQ、Kafka 的底层实现、功能异同、应用场景及技术选型分析

1️⃣ 引言 在现代分布式系统架构中&#xff0c;&#x1f4e9;消息队列&#xff08;MQ&#xff09;是不可或缺的组件。它在系统&#x1f517;解耦、&#x1f4c9;流量削峰、⏳异步处理等方面发挥着重要作用。目前&#xff0c;主流的消息队列系统包括 &#x1f680;RocketMQ、&…...

IDEA升级出现问题Failed to prepare an update Temp directory inside installation

IDEA升级出现问题"Failed to prepare an update Temp directory inside installation…" 问题来源&#xff1a; 之前修改了IDEA的默认配置文件路径&#xff0c;然后升级新版本时就无法升级&#xff0c;提示"Failed to prepare an update Temp directory insid…...

DeepSeek提示词手册

一、核心原则&#xff1a;基于DeepSeek的推理特性 自然语言优先undefinedDeepSeek擅长理解自然表达&#xff0c;无需复杂模板。例如&#xff1a; ❌旧模板&#xff1a;"你是专业分析师&#xff0c;需分三步回答&#xff0c;第一步…" ✅高效提问&#xff1a;"…...

基于UVM搭验证环境

基于UVM搭验证环境基本思路&#xff1a; 首先&#xff0c;我们搭建环境时一般都有一个目标的DUT。此时&#xff0c;我们可以结合所要验证的的模块、是否需要VIP、验证侧重点等在典型的UVM验证环境的基础上做适当调整后形成一个大体的环境架构。比如&#xff0c;需要一个ahb_vip…...

C++性能优化—人工底稿版

C以高性能著称&#xff0c;性能优化是C程序员绕不过去的一个话题&#xff0c;性能优化是一个复杂、全局而又细节的问题&#xff0c;本文总结C性能分析中常用的知识。 性能优化的时机 大部分关于性能优化的文章都强调&#xff1a;不要过早的进行性能优化。 C编码层面 数据结…...

Java 使用腾讯翻译 API 实现含 HTML 标签文本精准翻译工具

一、翻译标签文本工具 package org.springblade.common.utils;import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern;public class TencentTranslationForHTML {public static void main(String[] args) {Stri…...

十款开源的论坛建站工具

以下是十款开源的论坛建站工具&#xff0c;它们各具特色&#xff0c;能够满足不同用户的需求&#xff1a; Discuz!&#xff08;Crossday Discuz! Board&#xff09; 特点&#xff1a;基础架构采用web编程组合PHPMySQL&#xff0c;用户可以在不需要任何编程的基础上&#xff0c;…...

vue学习6

1. 智慧商城 1. 路由设计配置 单个页面&#xff0c;独立展示的&#xff0c;是一级路由 2.二级路由配置 规则&组件配置导航链接配置路由出口 <template><div id"app"><!--二级路由出口--><router-view></router-view><van-…...

线程池以及日志、线程总结

一、线程池以及日志 1、基础线程池写法 主线程在main函数中构建一个线程池&#xff0c;初始化(Init)后开始工作(Start) 此时线程池中每个线程都已经工作起来了&#xff0c;只是任务队列中任务为空&#xff0c;所有线程处于休眠状态(通过线程同步中的条件变量实现&#xff0c…...

Vue 响应式渲染 - 过滤应用

Vue 渐进式JavaScript 框架 基于Vue2的学习笔记 - Vue响应式渲染综合 - 过滤应用 目录 过滤应用 引入vue Vue设置 设置页面元素 模糊查询过滤实现 函数表达式实现 总结 过滤应用 综合响应式渲染做一个输入框&#xff0c;用来实现&#xff1b;搜索输入框关键词符合列表。…...

【ThreeJS Basics 1-3】Hello ThreeJS,实现第一个场景

文章目录 环境创建一个项目安装依赖基础 Web 页面概念解释编写代码运行项目 环境 我的环境是 node version 22 创建一个项目 首先&#xff0c;新建一个空的文件夹&#xff0c;然后 npm init -y , 此时会快速生成好默认的 package.json 安装依赖 在新建的项目下用 npm 安装依…...

银行国际结算

银行国结项目&#xff0c;即国际结算项目&#xff0c;是银行业务中的重要组成部分&#xff0c;它涉及跨国界的货币收付和资金转移。 一、银行国结项目的定义 银行国结项目是指银行为国际贸易、投资等活动提供的国际结算服务&#xff0c;包括各种国际支付和资金清算业务。这些…...

Go语言构建微服务:从入门到实战

引言 在云原生时代&#xff0c;微服务架构已成为构建复杂分布式系统的首选方案。Go语言凭借其卓越的并发支持、简洁的语法和高效的运行时&#xff0c;成为微服务开发的利器。本文将深入探讨如何用Go构建健壮的微服务系统&#xff0c;并通过完整案例演示关键实现细节。 一、微…...

深入理解动态代理

为什么需要动态代理 对于代码的增强逻辑我们是清楚具体实现的,一种方式是增强逻辑作为委托类,被其他业务类调用, 这样会有很多重复代码,而且,当需要根据动态参数来决定增强逻辑时,重复代码会更多,逻辑会更不清晰 二,也是动态代理产生的原始需求,解决类爆照问题, 所以…...

私有属性和方法(python)

一、私有属性&#xff08;属性名前面加两个短下划线&#xff09; &#xff08;一&#xff09;私有属性与公有属性区别 公有属性&#xff1a;在类里面和外面均可以访问和修改 私有属性&#xff1a;需要用set方法才能修改&#xff0c;get方法才能访问 &#xff08;二&#xf…...

Cherry Studio之DeepSeek联网/本地,建属于自己的AI助理!

上一篇文章&#xff0c;讲了DeepSeek-R1部署到本地的方法。这一篇文章&#xff0c;我们让DeepSeek再一次升级&#xff0c;通过图形化界面来交互&#xff0c;从而变成我们的AI助理&#xff0c;让DeepSeek R1发挥最大实力&#xff01; 首选需要借助硅基流动的API接口&#xff0c…...

TcpClientTest

ClientTest&#xff1a; using System; using System.Net.Sockets; using System.Text;class TcpClientTest {static void Main(string[] args){try{// 创建一个TcpClient实例并连接到服务器 TcpClient client new TcpClient("1vg5062570.51mypc.cn", 43319);//1v…...

IGBT的两级关断

IGBT&#xff08;绝缘栅双极型晶体管&#xff09;的两级关断&#xff08;Two-stage turn-off&#xff09;是一种优化关断过程的方法&#xff0c;主要用于减少关断时的电压过冲和dv/dt&#xff08;电压变化率&#xff09;过高的问题&#xff0c;特别是在大功率应用中&#xff08…...

【STM32】ADC

本次实现的是ADC实现数字信号与模拟信号的转化&#xff0c;数字信号时不连续的&#xff0c;模拟信号是连续的。 1.ADC转化的原理 模拟-数字转换技术使用的是逐次逼近法&#xff0c;使用二分比较的方法来确定电压值 当单片机对应的参考电压为3.3v时&#xff0c;0~ 3.3v(模拟信号…...

从MyBatis-Plus看Spring Boot自动配置原理

一、问题引入&#xff1a;神秘的配置生效之谜 当我们使用MyBatis-Plus时&#xff0c;只需在pom.xml中添加依赖&#xff1a; <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3…...

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 0基础…...

雪花算法应用

什么是雪花算法&#xff1f; 雪花算法是由 Twitter 开源的分布式 ID 生成算法&#xff0c;用于生成 64 位的长整型唯一 ID。其结构如下&#xff1a; - 1 位符号位&#xff1a;始终为 0 - 41 位时间戳&#xff1a;精确到毫秒 - 10 位工作机器 ID&#xff1a;包含 5 位数据中心 …...

Chapter3:结构化程序设计

参考书籍&#xff1a;《C#边做边学》&#xff1b; 3.结构化程序设计 3.1 结构化程序设计的3种基本结构 顺序结构&#xff1a;先执行 A {\rm A} A语句&#xff0c;再执行 B {\rm B} B语句&#xff0c;两者是顺序执行的关系&#xff1b; 选择结构&#xff1a;根据所定选择条件为…...

白话文实战Nacos(保姆级教程)

前言 上一篇博客 我们创建好了微服务项目,本篇博客来体验一下Nacos作为注册中心和配置中心的功能。 注册中心 如果我们启动了一个Nacos注册中心,那么微服务比如订单服务,启动后就可以连上注册中心把自己注册上去,这过程就是服务注册。每个微服务,比如商品服务都应该注册…...

c语言函数学习

C语言函数学习笔记&#xff1a;从入门到实践 一、什么是函数&#xff1f; 函数是C语言中用于封装特定功能的代码块&#xff0c;是模块化编程的核心。通过函数可以实现&#xff1a; 代码复用&#xff1a;避免重复编写相同逻辑 逻辑清晰&#xff1a;将复杂程序分解为多个小模块…...