并查集

Posted Kristin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并查集相关的知识,希望对你有一定的参考价值。

并查集--basic

public class UnionFind1 {
    private int[] parent;   //数组,表示并查集所有元素的集合号

    private int size;   //表示并查集元素个数

    public UnionFind1(int size) { //并查集初始化
        this.size = size;
        parent = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
        }
    }

    /**
     * 查看元素所属集合
     *
     * @param element
     * @return
     */
    private int find(int element) {
        return parent[element];
    }

    /**
     * 判断两个集合是否在同一个集合
     *
     * @param firstElement
     * @param secondElement
     * @return
     */
    public boolean isConnected(int firstElement, int secondElement) {
        return find(firstElement) == find(secondElement);
    }

    /**
     * 合并firstElement,secondElement两个元素所在集合
     * 将firstElement集合中所有元素的集合号都变为secondElement,就是认为是将两个集合合并
     *
     * @param firstElement
     * @param secondElement
     */
    public void unionElements(int firstElement, int secondElement) {
        int firstUnion = find(firstElement);
        int secondUnion = find(secondElement);
        if (firstUnion != secondUnion) {
            for (int i = 0; i < size; i++) {
                if (parent[i] == firstUnion) {
                    parent[i] = secondUnion;
                }
            }
        }
    }

    private void printArray() {
        for (int id : this.parent) {
            System.out.print(id + "\t");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int n = 10;
        UnionFind1 union = new UnionFind1(n);
        System.out.println("初始:");
        union.printArray();

        System.out.println("连接了5 6");
        union.unionElements(5, 6);
        union.printArray();

        System.out.println("连接了1 2");
        union.unionElements(1, 2);
        union.printArray();

        System.out.println("连接了2 3");
        union.unionElements(2, 3);
        union.printArray();

        System.out.println("连接了1 4");
        union.unionElements(1, 4);
        union.printArray();

        System.out.println("连接了1 5");
        union.unionElements(1, 5);
        union.printArray();

        System.out.println("1  6 是否连接:" + union.isConnected(1, 6));

        System.out.println("1  8 是否连接:" + union.isConnected(1, 8));
    }
}

快速合并

public class UnionFind2 {
    private int[] parent;
    private int size;

    public UnionFind2(int size) { //初始化并查集
        this.size = size;
        parent = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
        }
    }

    /**
     * 查找element集合的头结点
     * element=parent[element]:说明element元素是一个集合的头结点或者是自己一个集合
     *
     * @param element
     * @return
     */
    public int find(int element) {
        while (element != parent[element]) {//此时说明element已不是自己一个结点了,所以通过循环找寻element所属集合的头结点
            element = parent[element];
        }
        return element;  //
    }

    /**
     * 判断两个元素是否是一个集合:只需要判断两个元素所属集合的头结点是否相同即可
     *
     * @param firstElement
     * @param secondElement
     * @return
     */
    public boolean isConnected(int firstElement, int secondElement) {
        int firstUnion = find(firstElement);
        int secondUnion = find(secondElement);
        return firstUnion == secondUnion;
    }

    /**
     * 合并firstElement,secondElement所在的两个集合
     * 将firstElement集合的头结点指向secondElement集合的头结点
     *
     * @param firstElement
     * @param secondElement
     */
    public void unionElements(int firstElement, int secondElement) {
        int firstUnion = find(firstElement);
        int secondUnion = find(secondElement);
        if (firstUnion != secondUnion) {
            parent[firstElement] = secondUnion;
        }
    }

    /**
     * 打印每个元素所属集合的小组号,即它的上一个结点号
     */
    private void printArray() {
        for (int parent : parent) {
            System.out.print(parent + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int n = 10;
        UnionFind2 union = new UnionFind2(n);
        System.out.println("初始:");
        union.printArray();

        System.out.println("连接了5 6");
        union.unionElements(5, 6);
        union.printArray();

        System.out.println("连接了1 2");
        union.unionElements(1, 2);
        union.printArray();

        System.out.println("连接了2 3");
        union.unionElements(2, 3);
        union.printArray();

        System.out.println("连接了1 4");
        union.unionElements(1, 4);
        union.printArray();

        System.out.println("连接了1 5");
        union.unionElements(1, 5);
        union.printArray();

        System.out.println("1  6 是否连接:" + union.isConnected(1, 6));

        System.out.println("1  8 是否连接:" + union.isConnected(1, 8));
    }
}  

快速合并--weight

public class UnionFind3 {
    private int[] parent;
    private int[] weight;
    private int size;

    public UnionFind3(int size) {
        this.parent = new int[size];
        this.weight = new int[size];
        this.size = size;
        for (int i = 0; i < size; i++) {
            this.parent[i] = i;
            this.weight[i] = 1;
        }
    }

    /**
     * 查找元素所属集合的头结点
     *
     * @param element
     * @return
     */
    public int find(int element) {
        while (element != parent[element]) {
            element = parent[element];
        }
        return element;
    }

    /**
     * 判断两个元素是否是一个集合
     *
     * @param firstElement
     * @param secondElement
     * @return
     */
    public boolean isConnected(int firstElement, int secondElement) {
        return find(firstElement) == find(secondElement);
    }

    public void unionElements(int firstElement, int secondElement) {
        int firstRoot = find(firstElement);
        int secondRoot = find(secondElement);
        if (firstRoot == secondRoot) {//已经是同一个集合的元素就不用再合并了
            return;
        }

        //集合的子元素多的头结点,合并之后仍然做头结点
        if (weight[firstRoot] > weight[secondRoot]) {
            parent[secondRoot] = firstRoot;
            weight[firstRoot] += weight[secondRoot];
        } else {
            parent[firstRoot] = secondRoot;
            weight[secondRoot] += weight[firstRoot];
        }
    }

    /**
     * 打印parent数组
     */
    public void printArray(int[] arr) {
        for (int parent : arr) {
            System.out.print(parent + "\t");
        }
        System.out.println();
    }


    public static void main(String[] args) {
        int n = 10;
        UnionFind3 union = new UnionFind3(n);

        System.out.println("初始parent:");
        union.printArray(union.parent);
        System.out.println("初始weight:");
        union.printArray(union.weight);

        System.out.println("连接了5 6 之后的parent:");
        union.unionElements(5, 6);
        union.printArray(union.parent);
        System.out.println("连接了5 6 之后的weight:");
        union.printArray(union.weight);

        System.out.println("连接了1 2 之后的parent:");
        union.unionElements(1, 2);
        union.printArray(union.parent);
        System.out.println("连接了1 2 之后的weight:");
        union.printArray(union.weight);

        System.out.println("连接了2 3 之后的parent:");
        union.unionElements(2, 3);
        union.printArray(union.parent);
        System.out.println("连接了2 3 之后的weight:");
        union.printArray(union.weight);

        System.out.println("连接了1 4 之后的parent:");
        union.unionElements(1, 4);
        union.printArray(union.parent);
        System.out.println("连接了1 4 之后的weight:");
        union.printArray(union.weight);

        System.out.println("连接了1 5 之后的parent:");
        union.unionElements(1, 5);
        union.printArray(union.parent);
        System.out.println("连接了1 5 之后的weight:");
        union.printArray(union.weight);

        System.out.println("1  6 是否连接:" + union.isConnected(1, 6));

        System.out.println("1  8 是否连接:" + union.isConnected(1, 8));
    }
}

快速合并--height

public class UnionFind4 {
    private int[] parent;
    private int[] height;
    int size;

    public UnionFind4(int size) {
        this.size = size;
        this.parent = new int[size];
        this.height = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
            height[i] = 1;
        }
    }

    public int find(int element) {
        while (element != parent[element]) {
            element = parent[element];
        }
        return element;
    }

    public boolean isConnected(int firstElement, int secondElement) {
        return find(firstElement) == find(secondElement);

    }

    public void unionElements(int firstElement, int secondElement) {
        int firstRoot = find(firstElement);
        int secondRoot = find(secondElement);
        if (firstRoot == secondRoot) {
            return;
        }
        if (height[firstRoot] > height[secondRoot]) {
            parent[secondRoot] = firstRoot;
        } else if (height[firstRoot] < height[secondRoot]) {
            parent[firstRoot] = secondRoot;
        } else {
            parent[firstRoot] = secondRoot;
            height[secondRoot] += 1;
        }
    }

    private void printArray(int[] arr) {
        for (int parent : arr) {
            System.out.print(parent + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int n = 10;
        UnionFind4 union = new UnionFind4(n);

        System.out.println("初始parent:");
        union.printArray(union.parent);
        System.out.println("初始height:");
        union.printArray(union.height);

        System.out.println("连接了5 6 之后的parent:");
        union.unionElements(5, 6);
        union.printArray(union.parent);
        System.out.println("连接了5 6 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 2 之后的parent:");
        union.unionElements(1, 2);
        union.printArray(union.parent);
        System.out.println("连接了1 2 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了2 3 之后的parent:");
        union.unionElements(2, 3);
        union.printArray(union.parent);
        System.out.println("连接了2 3 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 4 之后的parent:");
        union.unionElements(1, 4);
        union.printArray(union.parent);
        System.out.println("连接了1 4 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 5 之后的parent:");
        union.unionElements(1, 5);
        union.printArray(union.parent);
        System.out.println("连接了1 5 之后的height:");
        union.printArray(union.height);

        System.out.println("1  6 是否连接:" + union.isConnected(1, 6));

        System.out.println("1  8 是否连接:" + union.isConnected(1, 8));
    }
}

路径压缩

public class UnionFind5 {
    private int[] parent;
    private int[] height;
    int size;

    public UnionFind5(int size) {
        this.size = size;
        this.parent = new int[size];
        this.height = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
            height[i] = 1;
        }
    }

    public int find(int element) {
        while (element != parent[element]) {
            parent[element]=parent[parent[element]];//对于height比较大的,可以压缩路径
            element = parent[element];
        }
        return element;
    }

    public boolean isConnected(int firstElement, int secondElement) {
        return find(firstElement) == find(secondElement);

    }

    public void unionElements(int firstElement, int secondElement) {
        int firstRoot = find(firstElement);
        int secondRoot = find(secondElement);
        if (firstRoot == secondRoot) {
            return;
        }
        if (height[firstRoot] > height[secondRoot]) {
            parent[secondRoot] = firstRoot;
        } else if (height[firstRoot] < height[secondRoot]) {
            parent[firstRoot] = secondRoot;
        } else {
            parent[firstRoot] = secondRoot;
            height[secondRoot] += 1;
        }
    }

    private void printArray(int[] arr) {
        for (int parent : arr) {
            System.out.print(parent + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        int n = 10;
        UnionFind5 union = new UnionFind5(n);

        System.out.println("初始parent:");
        union.printArray(union.parent);
        System.out.println("初始height:");
        union.printArray(union.height);

        System.out.println("连接了5 6 之后的parent:");
        union.unionElements(5, 6);
        union.printArray(union.parent);
        System.out.println("连接了5 6 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 2 之后的parent:");
        union.unionElements(1, 2);
        union.printArray(union.parent);
        System.out.println("连接了1 2 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了2 3 之后的parent:");
        union.unionElements(2, 3);
        union.printArray(union.parent);
        System.out.println("连接了2 3 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 4 之后的parent:");
        union.unionElements(1, 4);
        union.printArray(union.parent);
        System.out.println("连接了1 4 之后的height:");
        union.printArray(union.height);

        System.out.println("连接了1 5 之后的parent:");
        union.unionElements(1, 5);
        union.printArray(union.parent);
        System.out.println("连接了1 5 之后的height:");
        union.printArray(union.height);

        System.out.println("1  6 是否连接:" + union.isConnected(1, 6));

        System.out.println("1  8 是否连接:" + union.isConnected(1, 8));
    }
}

例题

例题:How Many Tables

public class Main {
    private int[] parent;
    private int[] weight;
    private int size;
    private int groups;

    public Main(int size) {
        this.size = size;
        this.groups = size;
        this.parent = new int[size];
        this.weight = new int[size];
        for (int i = 0; i < size; i++) {
            parent[i] = i;
            weight[i] = 1;
        }
    }

    public int find(int element) {
        while (element != parent[element]) {
            element = parent[element];
        }
        return element;
    }

    public boolean isConnected(int firstElement, int secondElement) {
        return find(firstElement) == find(secondElement);
    }

    public void unionElement(int firstElement, int secondElement) {
        int firstRoot = find(firstElement);
        int secondRoot = find(secondElement);
        if (firstRoot == secondRoot) {
            return;
        }
        if (weight[firstRoot] < weight[secondRoot]) {
            parent[firstRoot] = secondRoot;
            weight[secondRoot] += weight[firstRoot];
        } else {
            parent[secondRoot] = firstRoot;
            weight[firstRoot] += weight[secondRoot];
        }
        this.groups--;
    }

    public int getGroups() {
        return this.groups;
    }

    public void printArray(int[] arr) {
        for (int parent : arr) {
            System.out.print(parent + " ");
        }
        System.out.println();
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        for (int i = 0; i < n; i++) {
            int N = scan.nextInt();
            Main m = new Main(N);
            int M = scan.nextInt();
            for (int j = 0; j < M; j++) {
                int a = scan.nextInt()-1;
                int b = scan.nextInt()-1;
                m.unionElement(a, b);
            }
            System.out.println(m.getGroups());
        }
    }
}

  

 

以上是关于并查集的主要内容,如果未能解决你的问题,请参考以下文章

想要学会并查集吗?看我四十行代码实现它

树--12---并查集

笔记并查集---无向图处理代码模板及类型题

并查集

力扣 每日一题 886. 可能的二分法难度:中等,rating: 1794(并查集 / 拆点优化的扩展域并查集)

并查集