LeetCode——并查集

前言

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并及查询问题。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

操作

  • Init:初始化
    parent,数组(字典),保存对应节点所在树的根节点;
    rank,数组(字典),保存对应节点作为根的树的深度,有些特殊情况会有需求;
  • Union:合并
    将两个节点所在的树合并成一棵树,修改rank中树的深度,parent中树的根节点;
  • Find:查找
    通过parent查找元素所在树的根节点。
class UFS {
    private int[] parent;

    public UFS(int n) {
        parent = new int[n + 1];
        for (int i = 0; i < parent.length; i++) {
            parent[i] = i;
        }
    }

    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    public boolean union(int x, int y) {
        int xp = find(x), yp = find(y);
        if (xp == yp) {
            return false;
        } else {
            parent[xp] = yp;
            return true;
        }
    }
}

684. 冗余连接

public int[] findRedundantConnection(int[][] edges) {
    UFS ufs = new UFS(edges.length);
    for (int[] edge : edges) {
        if (!ufs.union(edge[0], edge[1])) {//如果合并失败,两棵树出现交集,即出现冗余边
            return edge;
        }
    }
    return null;
}

685. 冗余连接 II

首先明确树与图的区别,树中不存在入度超过1的节点,也不存在环路(任一枝上都需要存在至少一个出度为0的节点);由于题目中说明删除的edge应该是最后出现的,所以可以覆盖式记录,根据题意,只存在不超过1个冲突边和环路。
存在3种冗余情况:

  • 只存在环路,删除最后出现的环路组成边;
  • 只存在入度大于2的冲突边,删除最后出现的冲突边;
  • 既存在环路,也存在冲突,删除先出现的冲突边
public int[] findRedundantDirectedConnection(int[][] edges) {
    int nodeCount = edges.length;
    UFS ufs = new UFS(nodeCount);
    HashMap parents = new HashMap();
    int conflict = -1, cycle = -1;
    for (int i = 0; i < nodeCount; i++) {
        if (parents.get(edges[i][1]) != null) {、
            //记录入度超过1的节点,如果同时也是闭环边,后面不应该检测出环;
            //如果检测出环,另一条冲突边才是环内边,同时解决环和冲突,另一条冲突边是冗余边
            conflict = i;
        } else {
            parents.put(edges[i][1], edges[i][0]);
            if (!ufs.union(edges[i][0], edges[i][1])) {
                cycle = i;//记录环路节点的闭环边
            }
        }
    }
    if (conflict < 0) {
        //不存在冲突边,因为必然存在冗余边,所以必然存在环路
        return edges[cycle];//返回最后出现的环路边
    } else {
        //存在入度超过1的节点
        if (cycle > 0) {
            //存在环路,该冲突边肯定不是环路中的,不然不应该检测出环,所以返回第一条冲突边
            return new int[]{parents.get(edges[conflict][1]), edges[conflict][1]};
        } else {
            //冲突,无环,要么只存在冲突,要么冲突边也存在环内,所以,冲突边是冗余边
            return edges[conflict];//返回最后出现的冲突边
        }
    }
}

参考文章

算法学习笔记(1) : 并查集
并查集
冗余连接 II

你可能感兴趣的:(LeetCode——并查集)