java-并查集(Disjoint-set)-将多个集合合并成没有交集的集合


import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


public class DisjointSet {

/**
题目:给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
(1)请描述你解决这个问题的思路;
(2)请给出主要的处理流程,算法,以及算法的复杂度
(3)请描述可能的改进。
解答:
1. 假定每个集合编号为0,1,2,3...
2. 创建一个hash_map,key为字符串,value为一个链表,链表节点为字符串所在集合的编号。遍历所有的集合,将字符串和对应的集合编号插入到hash_map中去。
3. 创建一个长度等于集合个数的int数组,表示集合间的合并关系。例如,下标为5的元素值为3,表示将下标为5的集合合并到下标为3的集合中去。开始时将所有值都初始化为-1,表示集合间没有互相合并。在集合合并的过程中,我们将所有的字符串都合并到编号较小的集合中去。
遍历第二步中生成的hash_map,对于每个value中的链表,首先找到最小的集合编号(有些集合已经被合并过,需要顺着合并关系数组找到合并后的集合编号),然后将链表中所有编号的集合都合并到编号最小的集合中(通过更改合并关系数组)。
4.现在合并关系数组中值为-1的集合即为最终的集合,它的元素来源于所有直接或间接指向它的集合。
0: {aaa bbb ccc}
1: {bbb ddd}
2: {eee fff}
3: {ggg}
4: {ddd hhh}
生成的hash_map,和处理完每个值后的合并关系数组分别为
aaa: 0 [-1, -1, -1, -1, -1]
bbb: 0, 1 [-1, 0, -1, -1, -1]
ccc: 0 [-1, 0, -1, -1, -1]
ddd: 1, 4 [-1, 0, -1, -1, 0]
eee: 2 [-1, 0, -1, -1, 0]
fff: 2 [-1, 0, -1, -1, 0]
ggg: 3 [-1, 0, -1, -1, 0]
hhh: 4 [-1, 0, -1, -1, 0]
所以合并完后有三个集合,第0,1,4个集合合并到了一起,
第2,3个集合没有进行合并。
Use "Disjoin-set".But I use "HashSet" and "HashMap" of Java API.Does "Disjoin-set" have its own data structure?
see also [url]http://www.csie.ntnu.edu.tw/~u91029/DisjointSets.html[/url]
*/
private final int SIZE=7;
private int[] father;//the root in disjion set.
private static List<Set<String>> resultList=new ArrayList<Set<String>>();

public static void main(String[] args) {
String[] str0={
"aaa",
"bbb",
"ccc",};
String[] str1={
"bbb",
"ddd",};
String[] str2={
"eee",
"fff",};
String[] str3={
"ggg",};
String[] str4={
"ddd",
"hhh",};
String[] str5={
"xx",
"yy",};
String[] str6={
"zz",
"yy",};
String[][] strs={str0,str1,str2,str3,str4,str5,str6};
//change String[][] to List<Set>
for(String[] str:strs){
//when I write--"Arraylist list=Arrays.asList(strArray)","addAll()" is unsupported for such a arraylist.
Set<String> set=new HashSet<String>();
set.addAll(Arrays.asList(str));
resultList.add(set);
}
DisjointSet disjointSet=new DisjointSet();
disjointSet.disjoin(strs);
}

public void disjoin(String[][] strings){
if(strings==null||strings.length<2)return;
initial();
Map<String,List<Integer>> map=storeInHashMap(strings);
union(map);
}

//in the beginning,each element is in its own "group".
public void initial(){
father=new int[SIZE];
for(int i=0;i<SIZE;i++){
father[i]=i;
}
}

/*Map<k,v>
* key:String
* value:List<Integer>-in which sets the string shows up.
*/
public Map<String,List<Integer>> storeInHashMap(String[][] strings){
Map<String,List<Integer>> map=new HashMap<String,List<Integer>>();
for(int i=0;i<SIZE;i++){
for(String each:strings[i]){
if(!map.containsKey(each)){
List<Integer> list=new ArrayList<Integer>();
list.add(i);
map.put(each, list);
}else{
map.get(each).add(i);
}
}
}
//traverse the hashmap
Iterator<Map.Entry<String, List<Integer>>> it=map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, List<Integer>> entry=it.next();
String key=entry.getKey();
List<Integer> value=entry.getValue();
System.out.println(key+":"+value);

}
return map;
}

public void union(Map<String,List<Integer>> map){
Iterator<Map.Entry<String, List<Integer>>> it=map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, List<Integer>> entry=it.next();
List<Integer> value=entry.getValue();
unionHelp(value);//the arrays whose indexes are in the same list should be merged to one set.
}
System.out.println("the father array is "+Arrays.toString(father));
//merge two sets
for(int i=0;i<SIZE;i++){
if(i!=father[i]){
Set<String> dest=resultList.get(father[i]);
Set<String> source=resultList.get(i);
dest.addAll(source);
}
}
//clear a set which has been added.
for(int i=0;i<SIZE;i++){
if(i!=father[i]){
resultList.get(i).clear();
}
}
System.out.println("after merge:"+resultList);
}

public void unionHelp(List<Integer> list){
int minFather=getFather(list.get(0));//list[0] is the smaller.
for(int i=0,size=list.size();i<size;i++){
father[list.get(i)]=minFather;
}
}

//general union in disjoin set.But we overload it in this case.
public void unionHelp(int x,int y){
if(father[x]!=father[y]){
int fx=getFather(x);
int fy=getFather(y);
//merge two arrays to the array that has a smaller index.
if(fx<fy){
father[y]=fx;
}else{
father[x]=fy;
}

}
}

public int getFather(int x){
while(x!=father[x]){
x=father[x];
}
return x;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我来解答你的问题。 并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。它支持的操作有三个: 1. makeSet(s): 建立一个新的并查集,其中包含 s 个单元素集合。 2. unionSet(x, y): 把元素 x 和元素 y 所在的集合合并,要求 x 和 y 所在的集合不相交,如果相交则不合并。 3. findSet(x): 找到元素 x 所在的集合的代表元素,该操作也可以用于判断两个元素是否在同一个集合中。 基于并查集,我们可以很方便地求解连通图个数。具体做法是,我们将图中的每个节点看作是一个集合中的元素,然后按照边的连接情况依次合并相邻的节点所在的集合。最后,连通图的个数就等于并查集集合的个数。 下面是并查集的 Python 代码实现: ```python class UnionFind: def __init__(self, n): self.parent = list(range(n)) self.count = n def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): rootX, rootY = self.find(x), self.find(y) if rootX == rootY: return self.parent[rootX] = rootY self.count -= 1 def getCount(self): return self.count ``` 其中,parent 数组存储了每个节点的父节点,count 表示当前并查集集合的个数。find() 方法采用路径压缩的方式查找元素所在的集合的代表元素,union() 方法将两个集合合并,getCount() 方法返回当前并查集集合的个数。 使用并查集求解连通图个数的 Python 代码实现如下: ```python def countComponents(n, edges): uf = UnionFind(n) for edge in edges: uf.union(edge[0], edge[1]) return uf.getCount() ``` 其中,n 表示节点的个数,edges 表示边的连接情况。我们用 UnionFind 类创建一个并查集,并依次合并每条边所连接的节点。最后,调用 getCount() 方法就可以得到连通图的个数。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值