LintCode刷题——集合合并(并查集)

题目描述

有一个集合组成的list,如果有两个集合有相同的元素,将他们合并。返回最后还剩下几个集合。

  • 集合数 n <= 1000
  • 每个集合的元素个数 m <= 100
  • 元素一定是非负整数,且不大于 100000
样例

样例1:

输入:list = [[1,2,3],[3,9,7],[4,5,10]]
输出:2 .
样例:剩下[1,2,3,9,7]和[4,5,10]这2个集合。

样例 2:

输入:list = [[1],[1,2,3],[4],[8,7,4,5]]
输出 :2
解释:剩下[1,2,3]和[4,5,7,8] 2个集合。

分析

一开始想直接暴力,后面发现会超时,就往并查集方向想,想将所有的数都设置一个所属的数组下标(相当于祖先节点),然后搜索一遍,如果相同的数的对应的数组下标不同,那么就合并这两个数所在的两个数组,当时没有想到给这个二维数组构造一个一维数组list然后对它按数进行从小到大排序,所以才导致了后面的结果出错。那么为啥要排序呢?因为经过排序之后,相同的数会靠在一起,这样就利于合并两个数组了,用一个while循环,只要遇到两个相同的数,而且这两个数对应的数组下标inde不相等,那么就将后面数所在数组的所有数合并,直到没有相同的数不在同一个数组之后就跳出while循环。最后统计一下list数组中有多少个不同的数下标(这里可以将list中的所有数依次遍历并添加到set集合中,因为set集合可以去重)即可。

 

暴力代码

 1 import java.util.*;
 2 
 3 public class Solution {
 4 
 5     public int setUnion(int[][] sets) {
 6         if (sets == null || sets.length == 0) {
 7             return 0;
 8         }
 9 
10         int n = sets.length;
11 
12         for (int i = 0; i < n - 1; i++) {
13             for (int j = i + 1; j < n; j++) {
14                 if (sets[i] == null || sets[j] == null) {
15                     continue;
16                 }
17                 if (find(sets[i], sets[j])) {
18                     sets[i] = union(sets[i], sets[j]);
19                     sets[j] = null;
20                     // 回溯
21                     i = -1;
22                     break;
23                 }
24             }
25         }
26         
27         int count = 0;
28         for (int i = 0; i < sets.length; i++) {
29             if (sets[i] != null) {
30             count++;
31             }
32         }
33         
34         return count;
35     }
36 
37     // 合并
38     public int[] union(int[] a, int[] b) {
39         Set<Integer> set = new HashSet<>();
40 
41         for (int i = 0; i < a.length; i++) {
42             set.add(a[i]);
43         }
44         for (int i = 0; i < b.length; i++) {
45             set.add(b[i]);
46         }
47 
48         int[] union = new int[set.size()];
49         Iterator<Integer> iterator = set.iterator();
50         int index = 0;
51         while (iterator.hasNext()) {
52             union[index++] = iterator.next();
53         }
54 
55         return union;
56     }
57 
58     // 查询
59     public boolean find(int[] a, int[] b) {
60         for (int i = 0; i < a.length; i++) {
61             for (int j = 0; j < b.length; j++) {
62                 if (a[i] == b[j]) {
63                     return true;
64                 }
65             }
66         }
67 
68         return false;
69     }
70 }

运行结果

AC代码

 1 import java.util.*;
 2 
 3 public class Solution {
 4    
 5    /**
 6     * 键值对类
 7     */ 
 8     class MyMap {
 9         int val;
10         int index;
11 
12         public MyMap(int val, int index) {
13             this.val = val;
14             this.index = index;
15         }
16     }
17 
18     /**
19      * @param sets:
20      *            Initial set list
21      * @return: The final number of sets
22      */
23     public int setUnion(int[][] sets) {
24         // Write your code here
25         if (sets == null || sets.length == 0) {
26             return 0;
27         }
28 
29         int n = sets.length;
30 
31         // 注意:一定要排序,才能用这种方法
32         // 定义一个MyMap类,val表示当前数,index表示当前数所在的数组的下标
33         List<MyMap> list = new ArrayList<>();
34         for (int i = 0; i < n; i++) {
35             for (int j = 0; j < sets[i].length; j++) {
36             list.add(new MyMap(sets[i][j], i));
37             }
38         }
39         
40         // 排序
41         Collections.sort(list, new Comparator<MyMap>() {
42 
43             @Override
44             public int compare(MyMap o1, MyMap o2) {
45                 if (o1.val > o2.val) {
46                     return 1;
47                 } else if (o1.val < o2.val){
48                     return -1;
49                 } else {            
50                     return 0;
51                 }
52             }
53             
54         });
55         
56         // 保存两个相同数的后一个数在list集合中的下标
57         int endIndex;
58         // 一直循环判断,直到没有相同的数为止,跳出循环
59         while ((endIndex = check(list)) != -1) {
60             int src = list.get(endIndex).index;
61             int target = list.get(endIndex - 1).index;
62             union(list, src, target);
63         }
64 
65         Set<Integer> set = new HashSet<>();
66         for (int i = 0; i < list.size(); i++) {
67             set.add(list.get(i).index);
68         }
69 
70         return set.size();
71     }
72 
73     // 返回要修改的那个数在list集合中的下标
74     public int check(List<MyMap> list) {
75         for (int i = 1; i < list.size(); i++) {
76             // 跟前面一个数比
77             // 如果当前数跟前面一个数相等,但是所在集合不是同一个,那么就要合并,并返回当前数所在的数组下标
78             if (list.get(i).val == list.get(i - 1).val && list.get(i).index != list.get(i - 1).index) {
79             return i;
80             }
81         }
82 
83         // 如果没有相等的数不在同一个数组中的话,那么就返回-1,即表示不需要合并
84         return -1;
85     }
86 
87     public void union(List<MyMap> list, int src, int target) {
88         for (int i = 0; i < list.size(); i++) {
89             if (list.get(i).index == src) {
90             list.get(i).index = target;
91             }
92         }
93     }
94 }

LintCode链接:https://www.lintcode.com/problem/1396/

posted @ 2021-04-14 19:20  没有你哪有我  阅读(148)  评论(0编辑  收藏  举报