leetcode - 684. 冗余连接

684. 冗余连接

解决思路

大致上的思路就是将元素加入到 并查集 中,那么在遍历到边的时候先去判断的边的两个端点的 根节点 是否相等,如果相等,那么就代表此刻把这条边加上去就形成了环【可以这么理解,如果形成了环,那么两个端点在环内从那个端点出发都可以到达环内所有节点,只是这里提取出共同的 根节点罢了】

并查集

并查集(Union Find):一种树型的数据结构,用于处理一些不交集(Disjoint Sets)的合并及查询问题。不交集指的是一系列没有重复元素的集合。

  • 合并(Union):将两个集合合并成一个集合。
  • 查找(Find):确定某个元素属于哪个集合。通常是返回集合内的一个「代表元素」。

并查集代码例子

java 复制代码
public class UnionFind {

    public static class Element<V> {
        public V value;

        public Element(V value) {
            this.value = value;
        }
    }

    public static class UnionFindSet<V> {
        public HashMap<V, Element<V>> elementMap;
        // key: 某个元素,value: 该元素的父
        public HashMap<Element<V>, Element<V>> fatherMap;
        // key: 某个集合的代表元素,value: 该集合的大小
        public HashMap<Element<V>, Integer> sizeMap;

        public UnionFindSet(List<V> list) {
            elementMap = new HashMap<>();
            fatherMap = new HashMap<>();
            sizeMap = new HashMap<>();
            for (V value : list) {
                Element<V> element = new Element<>(value);
                elementMap.put(value, element);
                // 初始化都是自己指向自己,也就是代表元素
                fatherMap.put(element, element);
                sizeMap.put(element, 1);
            }
        }

        /**
         * 给定一个 element,往上一直找,把代表元素(fatherMap 指向自己的为代表元素)返回
         * @param element
         * @return
         */
        public Element<V> findHead(Element<V> element) {
            Stack<Element<V>> path = new Stack<>();
            // 查找代表元素
            while (element != fatherMap.get(element)) {
                path.push(element);
                element = fatherMap.get(element);
            }
            while (!path.isEmpty()) {
                // 将路径上的 element,直接指向 代表元素,加快下次的查找速度
                fatherMap.put(path.pop(), element);
            }
            return element;
        }


        /**
         * 判断是不是在同一个集合
         * @return
         */
        public boolean isSameSet(V a, V b) {
            if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
                return findHead(elementMap.get(a)) == findHead(elementMap.get(b));
            }
            return false;
        }

        /**
         * 将两个集合合并,小的集合加入大的集合中
         * @param a
         * @param b
         */
        public void union(V a, V b) {
            if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
                Element aF = findHead(elementMap.get(a));
                Element bF = findHead(elementMap.get(b));
                if (aF != bF) {
                    Element<V> big = sizeMap.get(aF) >= sizeMap.get(bF) ? aF : bF;
                    Element<V> small = big == aF ? bF : aF;
                    fatherMap.put(small, big);
                    sizeMap.put(big, sizeMap.get(aF) + sizeMap.get(bF));
                    // 不是代表元素了,所以删除掉
                    sizeMap.remove(small);
                }
            }
        }
    }

}

解决

参考链接:. - 力扣(LeetCode)

java 复制代码
/**
 * 使用 并查集的 方法【参考官方】
 * 执行用时分布 1 ms 击败 62.07% 复杂度分析 消耗内存分布 42.10 MB 击败 61.45%
 * @param edges
 * @return
 */
public int[] findRedundantConnection(int[][] edges) {
    int[] parent = new int[edges.length + 1];
    // 初始化,将所有的节点指向自己
    for (int i = 0; i < parent.length; i++) {
        parent[i] = i;
    }
    for (int[] edge : edges) {
        int node1 = edge[0];
        int node2 = edge[1];
        // 如果父节点一样,代表已经形成环了
        if (findHead(parent, node1) == findHead(parent, node2)){
            return edge;
        }
        union(parent, edge);
    }
    return new int[0];
}

/**
 * 查找 根节点,并将 递归路径上的 节点都指向 根节点
 */
public int findHead(int[] parent, int element){
    if (parent[element] != element){
        // 递归获取根节点
        parent[element] = findHead(parent, parent[element]);
    }
    return parent[element];
}

/**
* 将数据加入到  并查集
*/
public void union(int[] parent, int[] edge){
  // 只是一个方向性的问题
  // 就是获取到 端点 0 的根节点,然后将它指向 端点 1 的根节点
  // 这种做法是为了直接指向根节点,如果是直接指向,会造成不是指向自己的根节点
  parent[findHead(parent, edge[0])] = findHead(parent, edge[1]);
}
相关推荐
.格子衫.4 小时前
Spring Boot 原理篇
java·spring boot·后端
多云几多4 小时前
Yudao单体项目 springboot Admin安全验证开启
java·spring boot·spring·springbootadmin
Swift社区5 小时前
LeetCode 394. 字符串解码(Decode String)
算法·leetcode·职场和发展
tt5555555555556 小时前
LeetCode进阶算法题解详解
算法·leetcode·职场和发展
让我们一起加油好吗6 小时前
【基础算法】DFS中的剪枝与优化
算法·深度优先·剪枝
Jabes.yang6 小时前
Java求职面试实战:从Spring Boot到微服务架构的技术探讨
java·数据库·spring boot·微服务·面试·消息队列·互联网大厂
聪明的笨猪猪6 小时前
Java Redis “高可用 — 主从复制”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
执尺量北斗6 小时前
[特殊字符] 基于 Qt + OpenGL 实现的入门级打砖块游戏
开发语言·qt·游戏
夏子曦6 小时前
C#内存管理深度解析:从栈堆原理到高性能编程实践
开发语言·c#
Q741_1476 小时前
C++ 模拟题 力扣495. 提莫攻击 题解 每日一题
c++·算法·leetcode·模拟