并查集

并查集知识小结,欢迎交流,指正错误。

1. 并查集

并查集(Union-Find Sets)是一种非常精巧而实用的数据结构,只有合并查找操作的数据结构,它主要用于处理一些不相交集合的合并问题。

可以查看该博客对并查集有个了解和认识,本人觉得该博客讲的还是不错的。

2. 并查集的实现

Java代码实现如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class UnionFind {

        private int[] record; // 记录每个元素的上一级是谁
        private int count; //剩余集合数目
		
    	// 初始化record[], 让每一个元素在最初始时的上一级是自己
        public UnionFind(int n) {
            record = new int[n];
            for (int i = 0;i < n;i++) {
                record[i] = i;
            }
            count = n;
        }

    	// 寻找 p 元素所在集合的"根元素"
        public int find(int p) {
            // 路径压缩
            if (p != record[p]) record[p] = find(record[p]);
            return record[p];
        }

    	// 合并 p 元素和 q 元素所在的集合
        public void union(int p, int q) {
            int pid = find(p);
            int qid = find(q);
            if (pid == qid) return;
            // 合并
            record[pid] = qid;
            count--;
        }

    	// 返回当前剩余的集合数
        public int count() {
            return count;
        }
}

3. 并查集的应用

题目描述:

某一群岛由 n 个小岛组成,为了加强小岛居民之间的交流,首领决定启动一个造桥工程,将全部 n 个小岛连接到一起。

由于受到经融危机的影响,头目要求造桥的总成本要最少,并且规定每一座桥的成本都不能超过 k 万金币。

需要注意的是,由于受到地理环境和气候影响,有些小岛之间没办法直接造桥。

现给定 m 条小岛之间的造桥成本数据以及 k 的值,请问该工程是否能够顺利完成?

注意:可能桥的数量不够,也可能费用超支。

输入描述:

多组输入,第一行输入一个正整数 T 表示输入数据的组数。

对于每一组输入数据:输入 m+1 行。

第一行包含3个正整数,分别表示 n、m 和 k ,n <= 100, m <= 1000, k <= 10000 ,数字之间用空格隔开。

接下来 m 行表示 m 条小岛之间的造桥成本数据,每一行包含3个正整数,分别表示两个小岛的编号(从1开始)和造桥的成本(单位:万)。

输出描述:

针对每一组输入数据,如果工程能够完成输出“YES”,否则输出“NO”。

基本思路:

可以认为能够成功造桥的两个岛之间属于同一个岛屿,即将这两个岛合并(union)到一个岛屿中。最后如果剩余的岛屿数量为 1 则表示将所有岛都成功连在一起,即该工程成功;否则工程失败。

实现代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Main{
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T = scanner.nextInt();
        for (int i = 0; i < T; i++) {
            int n = scanner.nextInt();
            int m = scanner.nextInt();
            int k = scanner.nextInt();
            UnionFind unionFind = new UnionFind(n);
            for (int j = 0; j < m; j++) {
                int p = scanner.nextInt();
                int q = scanner.nextInt();
                int cost = scanner.nextInt();
                if (cost <= k) {
                    unionFind.union(p, q);
                }
            }
            if (unionFind.count() == 1) {
                System.out.println("YES");
            }else {
                System.out.println("NO");
            }
        }
    }
}
    class UnionFind {

        private int[] record; // 记录每个元素的上一级是谁
        private int count; // 岛屿的数量,

        public UnionFind(int n) {
            record = new int[n+1];
            for (int i = 0;i <= n;i++) {
                record[i] = i;
            }
            count = n;
        }

        public int find(int p) {
            // 路径压缩
            if (p != record[p]) record[p] = find(record[p]);
            return record[p];
        }

        public void union(int p, int q) {
            int pid = find(p);
            int qid = find(q);
            if (pid == qid) return;
            // 合并
            record[pid] = qid;
            count--;
        }

        public int count() {
            return count;
        }
    }
}

PS:当然该题也可以通过构造图的方法来解,最后用DFS来确认连通图的数量,为 1 表示成功;否则表示失败。

4. 其他

其他并查集的应用,LeetCode 547. 朋友圈

0%