20230329 5.3. 集合及运算

集合的表示

  • 集合运算:交、并、补、差、判定一个元素是否属于某一集合
  • 并查集:集合 并、查 某元素属于什么集合
  • 并查集问题中集合存储如何实现?
    • 可以用树结构表示集合,树的每个结点代表一个集合元素

集合运算

  • 采用数组存储形式
    • 双亲表示法:孩子指向双亲
    • 负数表示根结点;非负数表示双亲结点的下标。
      • 优化:负数大小表示集合内元素个数
  • 查找:查找某个元素所在的集合(用根结点表示)
  • 并运算:
    • 分别找到X1和X2两个元素所在集合树的根结点
    • 如果它们不同根,则将其中一个根结点的父结点指针设置成另一个根结点的数组下标
    • 合并优化:
      • 为了改善合并以后的查找性能,可以采用节点少的集合合并到节点多的集合中
      • 也可以根据高度,将树的高度较低的合并到较高的中
        • 如果执行了路径压缩,树的高度会变化
          • 路径压缩的目的是将从根节点到叶子节点的路径变短,也有多种方式
            • find 时,找到后直接将父引用指向所在树根节点

使用示例

@AllArgsConstructor
@ToString
public class SetType<T> {
    /**
     * 实体data
     */
    T data;

    /**
     * 父节点索引
     */
    int parent;
}
public class UnionSet<T> {
    SetType<T>[] setTypes;

    /**
     * 查找节点
     *
     * @param x
     * @return 返回根节点的索引
     */
    public int find(T x) {
        int maxSize = setTypes.length;
        int i;

        // 找到x所在的索引
        for (i = 0; i < maxSize && ObjectUtil.notEqual(x, setTypes[i].data); i++) ;

        if (i >= maxSize) {
            // 未找到X,返回-1
            return -1;
        }

        // 找到x所属集合的根的索引
        for (; setTypes[i].parent >= 0; i = setTypes[i].parent) ;

        return i;
    }

    /**
     * 查找节点,并进行路径压缩
     *
     * @param x
     * @return 返回根节点的索引
     */
    public int findAndCompress(T x) {
        int maxSize = setTypes.length;
        int i;

        // 找到x所在的索引
        for (i = 0; i < maxSize && ObjectUtil.notEqual(x, setTypes[i].data); i++) ;

        if (i >= maxSize) {
            // 未找到X,返回-1
            return -1;
        }

        int j = i;

        // 找到x所属集合的根的索引
        for (; setTypes[i].parent >= 0; i = setTypes[i].parent) ;


        // 路径压缩,将被查找节点的父引用指向根节点
        setTypes[j].parent = i;

        return i;
    }


    /**
     * 判断两个元素是否属于同一个集合
     *
     * @param x1
     * @param x2
     * @return
     */
    public boolean isConnected(T x1, T x2) {
        return find(x1) == find(x2);
    }

    /**
     * 合并
     * <p>
     * 基于树的节点个数,小的集合合并到大的集合中
     *
     * @param x1
     * @param x2
     */
    public void union(T x1, T x2) {
        int root1 = find(x1);
        int root2 = find(x2);

        if (root1 != root2) {
            int x1Parent = setTypes[root1].parent;
            int x2Parent = setTypes[root2].parent;

            // x1和x2不属于同一个子集时,需要合并
            // 小的集合合并到相对大的集合中
            if (root1 <= root2) {
                setTypes[root2].parent = root1;
                setTypes[root1].parent = x1Parent + x2Parent;
            } else {
                setTypes[root1].parent = root2;
                setTypes[root2].parent = x1Parent + x2Parent;
            }
        }
    }
}
public class UnionSetMain {
    public static void main(String[] args) {
        UnionSet<Integer> unionSet = new UnionSet<>();
        unionSet.setTypes = new SetType[10];

        unionSet.setTypes[0] = new SetType(1, -4);
        unionSet.setTypes[1] = new SetType(2, 0);
        unionSet.setTypes[2] = new SetType(3, -3);
        unionSet.setTypes[3] = new SetType(4, 0);
        unionSet.setTypes[4] = new SetType(5, 2);
        unionSet.setTypes[5] = new SetType(6, -3);
        unionSet.setTypes[6] = new SetType(7, 0);
        unionSet.setTypes[7] = new SetType(8, 2);
        unionSet.setTypes[8] = new SetType(9, 5);
        unionSet.setTypes[9] = new SetType(10, 5);


        for (int i = 0; i < unionSet.setTypes.length; i++) {
            Console.log(i, unionSet.setTypes[i]);
        }

        unionSet.union(2, 3);

        Console.log("after union ====================");

        for (int i = 0; i < unionSet.setTypes.length; i++) {
            Console.log(i, unionSet.setTypes[i]);
        }

        unionSet.findAndCompress(5);


        Console.log("after compress ====================");

        for (int i = 0; i < unionSet.setTypes.length; i++) {
            Console.log(i, unionSet.setTypes[i]);
        }

    }


    @Test
    public void testCompress() {
        UnionSet<Integer> unionSet = new UnionSet<>();
        unionSet.setTypes = new SetType[10];

        unionSet.setTypes[0] = new SetType(0, -10);
        unionSet.setTypes[1] = new SetType(1, 0);
        unionSet.setTypes[2] = new SetType(2, 1);
        unionSet.setTypes[3] = new SetType(3, 2);
        unionSet.setTypes[4] = new SetType(4, 3);
        unionSet.setTypes[5] = new SetType(5, 4);
        unionSet.setTypes[6] = new SetType(6, 5);
        unionSet.setTypes[7] = new SetType(7, 6);
        unionSet.setTypes[8] = new SetType(8, 7);
        unionSet.setTypes[9] = new SetType(9, 8);

        unionSet.findAndCompress(5);
        unionSet.findAndCompress(5);

        Console.log("after compress ====================");

        for (int i = 0; i < unionSet.setTypes.length; i++) {
            Console.log(i, unionSet.setTypes[i]);
        }

    }
}

参考资料

posted @ 2023-06-21 16:23  流星<。)#)))≦  阅读(2)  评论(0编辑  收藏  举报