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

代码随想录Day56:图论(冗余连接、冗余连接II)

一、实战

108冗余连接

108. 冗余的边

思路:本题需要注意的是在本来就构成树的基础上,添加一条边(依然是n个节点,但有n条边),使这个图变成了有环图,那我们寻找冗余的边,那必然就只有一条。如果有多个答案,则返回二维数组中最后出现的边。

并查集功能:判断两个节点是否在一个集合,也可以将两个节点添加到一个集合中。

那么我们就可以从前向后遍历每一条边(因为优先让前面的边连上),边的两个节点如果不在同一个集合,就加入集合(即:同一个根节点)。如果边的两个节点已经出现在同一个集合里,说明着边的两个节点已经连在一起了,再加入这条边一定就出现环了

节点A 和节点 B 不在同一个集合,那么就可以将两个 节点连在一起
节点A 和 节点B已经在同一个集合(同一个根),如果将 节点A 和 节点B 连在一起就一定会出现环
package org.example;//运行时去掉import java.util.*;public class Main{public static void main(String[] args) {int N;int a,b;//构成边的两个节点Scanner scanner = new Scanner(System.in);N = scanner.nextInt();DisJoint disJoint = new DisJoint(N + 1);for (int i = 0; i < N; i++){a=scanner.nextInt();b=scanner.nextInt();if(disJoint.isSame(a,b))System.out.println(a+" "+b);else//不在同一个集合中,那就可以正常添加disJoint.join(a,b);}}}//并查集模板
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]));}public void join (int n, int m) {n = find(n);m = find(m);if (n == m) return;father[m] = n;}public boolean isSame(int n, int m){n = find(n);m = find(m);return n == m;}}

109冗余连接II

109. 冗余的边II

思路:与上一题相比,本题是一个有向图。本质是一个有向图,是由一颗有向树 + 一条有向边组成的 (所以此时这个图就不能称之为有向树),现在让我们找到那条边 把这条边删了,让这个图恢复为有向树。还有“若有多条边可以删除,请输出标准输入中最后出现的一条边”,这说明在两条边都可以删除的情况下,要删顺序靠后的边!

有向树的性质:只有根节点入度为0,其他节点入度都为1(因为该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点)。

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

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

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

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

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

删掉构成环的边就可
import java.util.*;public class Main {/*** 并查集模板*/static class Disjoint {private final int[] father;public Disjoint(int n) {father = new int[n];for (int i = 0; i < n; i++) {father[i] = i; // 初始化每个节点的父节点为其自身}}public void join(int n, int m) {n = find(n); // 查找n的根节点m = find(m); // 查找m的根节点if (n == m) return; // 如果根节点相同,则它们已经在同一个集合中father[n] = m; // 将n的根节点指向m的根节点,合并两个集合}public int find(int n) {return father[n] == n ? n : (father[n] = find(father[n])); // 路径压缩优化}public boolean isSame(int n, int m) {return find(n) == find(m); // 判断两个节点是否属于同一个集合}}static class Edge {int s;int t;public Edge(int s, int t) {this.s = s; // 边的起点this.t = t; // 边的终点}}static class Node {int id;int in; // 入度int out; // 出度(在这个问题中未使用)public Node() {in = 0;out = 0;}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);int n = scanner.nextInt(); // 输入节点数量nList<Edge> edges = new ArrayList<>();Node[] nodeMap = new Node[n + 1]; // 创建一个数组来存储每个节点的信息for (int i = 1; i <= n; i++) {nodeMap[i] = new Node();}Integer doubleIn = null; // 记录是否存在入度为2的节点// 遍历输入的所有边,记录每条边,并计算每个节点的入度for (int i = 0; i < n; i++) {int s = scanner.nextInt(); // 边的起点int t = scanner.nextInt(); // 边的终点nodeMap[t].in++; // 增加t节点的入度if (!(nodeMap[t].in < 2)) doubleIn = t; // 如果某个节点的入度达到2,记录该节点Edge edge = new Edge(s, t);edges.add(edge); // 将边加入到列表中}Edge result = null;// 如果存在入度为2的节点,则需要检查并解决这个问题if (doubleIn != null) {List<Edge> doubleInEdges = new ArrayList<>();for (Edge edge : edges) {if (edge.t == doubleIn) doubleInEdges.add(edge); // 找出入度为2的节点相关的边if (doubleInEdges.size() == 2) break; // 只需要找到两条边即可}Edge edge = doubleInEdges.get(1);// 检查移除某一边后,是否可以形成树if (isTreeWithExclude(edges, edge, nodeMap)) {result = edge;} else {result = doubleInEdges.get(0);}} else {// 如果没有入度为2的节点,则只需检查是否存在环result = getRemoveEdge(edges, nodeMap);}System.out.println(result.s + " " + result.t); // 输出需要删除的边}// 检查移除指定边之后,剩余的边能否构成一棵树public static boolean isTreeWithExclude(List<Edge> edges, Edge excludeEdge, Node[] nodeMap) {Disjoint disjoint = new Disjoint(nodeMap.length + 1);for (Edge edge : edges) {if (edge == excludeEdge) continue; // 跳过要排除的边if (disjoint.isSame(edge.s, edge.t)) {return false; // 如果发现环,则不能构成树}disjoint.join(edge.s, edge.t); // 合并两个节点}return true;}// 检查图中是否存在环,并返回导致环出现的边public static Edge getRemoveEdge(List<Edge> edges, Node[] nodeMap) {int length = nodeMap.length;Disjoint disjoint = new Disjoint(length);for (Edge edge : edges) {if (disjoint.isSame(edge.s, edge.t)) return edge; // 发现环则返回这条边disjoint.join(edge.s, edge.t); // 合并两个节点}return null;}
}

http://www.dtcms.com/a/339399.html

相关文章:

  • 【python】列表复制注意事项
  • 大模型+RPA:如何用AI实现企业流程自动化的“降本增效”?
  • 什么类型的项目会优先选择Headless CMS
  • 【habitat学习二】Habitat-Lab 快速入门指南(Quickstart)详解
  • 完美解决git报错拉取不到项目
  • 如何禁用 Windows 服务器的自动更新以避免意外重启
  • VMWare主机和客户机无法ping通
  • Android-ContentProvider的跨应用通信学习总结
  • Matplotlib数据可视化实战:Matplotlib安装与入门-跨平台环境配置与基本操作
  • 第四章:大模型(LLM)】07.Prompt工程-(2)Zero-shot Prompt
  • 【Linux】信号(二):Linux原生线程库相关接口
  • C#多线程学习—主子线程,Invoke与begininvoke
  • RabbitMQ:SpringBoot+RabbitMQ入门案例
  • 《用Proxy解构前端壁垒:跨框架状态共享库的从零到优之路》
  • vue3使用RouterLink跳转的时候,路径正确但是不显示对应内容
  • Computer Using Agents:数字世界中的超级用户——架构设计与产业实践
  • RNN深层困境:残差无效,Transformer为何能深层?
  • Qt猜数字游戏项目开发教程 - 从零开始构建趣味小游戏
  • 【数据库】通过‌phpMyAdmin‌管理Mysql数据
  • 概率论基础教程第5章 连续型随机变量(二)
  • 字节开源了一款具备长期记忆能力的多模态智能体:M3-Agent
  • RabbitMQ:SpringBoot+RabbitMQ Direct Exchange(直连型交换机)
  • 第7章 React性能优化核心
  • [langgraph]创建第一个agent
  • 如何在 Git Commit Message 中正确提及共同贡献者(Co-authored-by 实践指南)
  • 图解快速排序C语言实现
  • 数据结构----八大排序算法
  • 【报文构造】构造一个异常的IPV6报文,测试设备可靠性
  • 集成电路学习:什么是Object Tracking目标跟踪
  • 浙江电信IPTV天邑TY1613_高安版_晶晨S905L3SB_安卓9_原厂固件自改_线刷包