首页 > 其他分享 >【代码随想录Day54】图论Part06

【代码随想录Day54】图论Part06

时间:2024-10-27 14:48:00浏览次数:3  
标签:node parent int nodeU 随想录 Part06 edges Day54 nodeV

冗余连接

题目链接/文章讲解:代码随想录

import java.util.Scanner;

public class Main {
    private int numberOfNodes; // 节点数量
    private int[] parent; // 存储每个节点的父节点

    // 构造函数初始化并查集
    public Main(int size) {
        numberOfNodes = size;
        parent = new int[size + 1]; // 根据节点数量初始化数组
        initializeUnionFind();
    }

    // 初始化并查集,将每个节点的父节点设为自身
    private void initializeUnionFind() {
        for (int i = 0; i <= numberOfNodes; i++) {
            parent[i] = i;
        }
    }

    // 查找节点node的根节点,并进行路径压缩
    private int find(int node) {
        if (node == parent[node]) {
            return node;
        }
        // 路径压缩
        parent[node] = find(parent[node]);
        return parent[node];
    }

    // 判断节点nodeU和节点nodeV是否属于同一集合
    public boolean isSameSet(int nodeU, int nodeV) {
        return find(nodeU) == find(nodeV);
    }

    // 将节点nodeV连接到节点nodeU
    public void union(int nodeU, int nodeV) {
        int rootU = find(nodeU); // 寻找nodeU的根
        int rootV = find(nodeV); // 寻找nodeV的根
        if (rootU != rootV) {
            parent[rootV] = rootU; // 将nodeV的根节点设为nodeU的根节点
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int numberOfEdges = scanner.nextInt(); // 输入边的数量
        Main unionFind = new Main(numberOfEdges); // 初始化并查集

        for (int i = 0; i < numberOfEdges; i++) {
            int nodeU = scanner.nextInt(); // 输入节点U
            int nodeV = scanner.nextInt(); // 输入节点V
            if (unionFind.isSameSet(nodeU, nodeV)) {
                System.out.println(nodeU + " " + nodeV); // 如果nodeU和nodeV已连接,输出并结束
                return;
            } else {
                unionFind.union(nodeU, nodeV); // 否则将nodeU和nodeV连接
            }
        }
        scanner.close(); // 关闭扫描器
    }
}

冗余连接 II

题目链接/文章讲解:代码随想录

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main {
    private static int numberOfNodes;
    private static int[] parent;

    // 并查集初始化
    private static void initializeUnionFind() {
        for (int i = 1; i <= numberOfNodes; i++) {
            parent[i] = i;
        }
    }

    // 并查集里寻根的过程
    private static int find(int node) {
        if (node != parent[node]) {
            parent[node] = find(parent[node]); // 路径压缩
        }
        return parent[node];
    }

    // 将边 v -> u 加入并查集
    private static void union(int u, int v) {
        int rootU = find(u);
        int rootV = find(v);
        if (rootU != rootV) {
            parent[rootV] = rootU; // 将 v 的根指向 u 的根
        }
    }

    // 判断 u 和 v 是否找到同一个根
    private static boolean areInSameComponent(int u, int v) {
        return find(u) == find(v);
    }

    // 在有向图里找到删除的那条边,使其变成树
    private static void findEdgeToRemove(List<int[]> edges) {
        initializeUnionFind(); // 初始化并查集
        for (int i = 0; i < edges.size(); i++) { // 遍历所有的边
            int[] edge = edges.get(i);
            if (areInSameComponent(edge[0], edge[1])) { // 构成有向环了,就是要删除的边
                System.out.println(edge[0] + " " + edge[1]);
                return;
            } else {
                union(edge[0], edge[1]);
            }
        }
    }

    // 删一条边之后判断是不是树
    private static boolean isTreeAfterRemovingEdge(List<int[]> edges, int edgeToRemoveIndex) {
        initializeUnionFind(); // 初始化并查集
        for (int i = 0; i < edges.size(); i++) {
            if (i == edgeToRemoveIndex) continue; // 跳过要删除的边
            int[] edge = edges.get(i);
            if (areInSameComponent(edge[0], edge[1])) { // 构成有向环了,一定不是树
                return false;
            }
            union(edge[0], edge[1]);
        }
        return true;
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        numberOfNodes = scanner.nextInt();
        parent = new int[numberOfNodes + 1];
        List<int[]> edges = new ArrayList<>();
        int[] inDegree = new int[numberOfNodes + 1]; // 记录节点入度

        for (int i = 0; i < numberOfNodes; i++) {
            int startNode = scanner.nextInt();
            int endNode = scanner.nextInt();
            inDegree[endNode]++;
            edges.add(new int[]{startNode, endNode});
        }

        List<Integer> edgeIndicesWithInDegreeTwo = new ArrayList<>(); // 记录入度为2的边索引
        // 找入度为2的节点所对应的边,注意要倒序,因为优先删除最后出现的一条边
        for (int i = numberOfNodes - 1; i >= 0; i--) {
            if (inDegree[edges.get(i)[1]] == 2) {
                edgeIndicesWithInDegreeTwo.add(i);
            }
        }

        // 情况一、情况二
        if (!edgeIndicesWithInDegreeTwo.isEmpty()) {
            // 放在 edgeIndicesWithInDegreeTwo 里的边已经按照倒叙放的,所以这里就优先删第一个
            if (isTreeAfterRemovingEdge(edges, edgeIndicesWithInDegreeTwo.get(0))) {
                System.out.println(edges.get(edgeIndicesWithInDegreeTwo.get(0))[0] + " " + edges.get(edgeIndicesWithInDegreeTwo.get(0))[1]);
            } else {
                System.out.println(edges.get(edgeIndicesWithInDegreeTwo.get(1))[0] + " " + edges.get(edgeIndicesWithInDegreeTwo.get(1))[1]);
            }
            return;
        }

        // 处理情况三
        // 明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回
        findEdgeToRemove(edges);
    }
}

标签:node,parent,int,nodeU,随想录,Part06,edges,Day54,nodeV
From: https://blog.csdn.net/weixin_43724673/article/details/143268938

相关文章

  • 【代码随想录Day53】图论Part05
    并查集理论基础题目链接/文章讲解:并查集理论基础|代码随想录寻找存在的路径题目链接/文章讲解:代码随想录importjava.util.*;publicclassMain{publicstaticvoidmain(String[]args){intnumberOfElements,numberOfConnections;Scann......
  • 【代码随想录Day52】图论Part04
    字符串接龙题目链接/文章讲解:代码随想录importjava.util.*;publicclassMain{//使用广度优先搜索(BFS)方法计算从beginWord到endWord的最短转换序列长度publicstaticintfindLadderLength(StringbeginWord,StringendWord,List<String>wordList){......
  • 代码随想录算法训练营day26|455.分发饼干 376. 摆动序列 53. 最大子序和
    学习资料:https://programmercarl.com/贪心算法理论基础.html#算法公开课贪心算法Part1求局部最优解,最终达到全局最优455.分发饼干(大胃口吃大饼干)点击查看代码classSolution(object):deffindContentChildren(self,g,s):""":typeg:List[int]......
  • 代码随想录算法训练营第七天|LeetCode 344.反转字符串、LeetCode 541.反转字符串Ⅱ、
    LeetCode 344.反转字符串题目链接:LeetCode344.反转字符串文章链接:代码随想录题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用O(1)的额外空间解决这一问题。示......
  • 代码随想录刷题学习日记
    仅为个人记录复盘学习历程,解题思路来自代码随想录代码随想录刷题笔记总结网址:代码随想录二叉树的统一迭代法二叉树的统一迭代指的是二叉树的前序遍历,中序遍历,后序遍历使用迭代法实现时,在方法和形式上较为统一的迭代方法。二叉树的前序遍历,中序遍历,后序遍历之所以无法统一是......
  • 代码随想录算法训练营第24天(补第13天)|226.翻转二叉树, 101. 对称二叉树,104.二叉树的最
    226.翻转二叉树文章链接:https://programmercarl.com/0226.翻转二叉树.html#算法公开课题目链接:https://leetcode.cn/problems/invert-binary-tree/description/迭代法:这里使用了前序遍历来交换左右孩子节点classSolution{public:TreeNode*invertTree(TreeNode*r......
  • 代码随想录算法训练营day25| 491.递增子序列 46.全排列 47.全排列2
    学习资料:https://programmercarl.com/0491.递增子序列.html#算法公开课排列与组合的区别,不用startIndex,而每个树层都从0开始,但是要跳过已经用过的数(用used判断)学习记录:491.递增子序列(添加一个数组used(hash表),来保持数组每个位置上的数的使用情况,没用过为0,用过变成1)点击查看代......
  • 代码随想录算法训练营第24天(补第12天)| 递归遍历,迭代遍历,统一迭代
    前置知识二叉树的定义:structBNode{intval;BNode*lchild;BNode*rchild;BNode():lchild(NULL),rchild(NULL){}BNode(intval){val=val;lchild=rchild=NULL;}};递归遍历文章链接:https://programmercarl.com/二叉树的递归遍历.html#思路题目......
  • 代码随想录算法训练营第九天|leetcode151.翻转字符串里的单词、卡码网55.右旋字符串
    1leetcode151.翻转字符串里的单词题目链接:151.反转字符串中的单词-力扣(LeetCode)文章链接:代码随想录视频链接:字符串复杂操作拿捏了!|LeetCode:151.翻转字符串里的单词_哔哩哔哩_bilibili自己的思路:直接将空格去掉,然后分割字符串为列表,在列表中进行翻转,不在字符串内部操作,......
  • 代码随想录算法训练营第二十四天|Day24 回溯算法
    93.复原IP地址题目链接/文章讲解:https://programmercarl.com/0093.%E5%A4%8D%E5%8E%9FIP%E5%9C%B0%E5%9D%80.html视频讲解:https://www.bilibili.com/video/BV1XP4y1U73i/思路char**result;intresultTop;intsegments[3];intisValid(char*s,intstart,intend){......