问题描述:
线上遇到的一个问题,这里做一个变形。有若干行数据(2000w)左右,每行有若干个属性,比如姓名、邮箱、地址等等;如果两行数据有至少一个属性值相同,就认为这两个行是连通的;并且连通性有转移,比如A与B连通,B与C连通,就可以认为A与C连通。那么这两千万行数据哪些是连通的?
name | address | tel | … |
---|---|---|---|
A | abc | 135 | |
A | bcd | 136 | |
B | cde | 136 |
如上第一行与第二行name相同,可认为行1连通行2;
第二行与第三行第三个属性相同,可认为行2连通行3;
于是1 2 3可认为是一个组。
暴力解法:对每个属性遍历,对每行数据遍历,时间复杂度最少为 (n为行数)
思路1:看到连通性立马可以想到并查集,但是并查集是已经知道哪些行数据是连通的,然后再根据并查集构造的数判断哪些属于一个组,这是整体思路。对并查集不清楚可参考 https://blog.csdn.net/u011730199/article/details/80909373
https://blog.csdn.net/dm_vincent/article/details/7655764
接下来,如何判断行与行之间的连通性呢?
遍历做法直接舍弃。考虑到一行有若干属性,可以联想到树的层级结构。对每行数据,如果对应的属性值已经存在,则连通。判断属性值是否已经存在最快的是使用HashMap的key来判断。
每行数据有m个属性,初始化m个
String为属性值,int为行号。对每行数据判断对应层级的map,是否存在,若存在
uf.union(m1,m2);若不存在map.put(string,m);
时间复杂度为
m为属性个数,n为行数
如果想要判断哪些连通,需要使用BFS或者DFS。这里略过。
代码如下:
public class Vert {
private static ArrayList<HashMap<String,Integer>> list = new ArrayList<HashMap<String,Integer>>();
public static void main(String[] args) {
String[][] line = {{"136","[email protected]","[email protected]"},{"137","[email protected]","[email protected]"},
{"138","[email protected]","[email protected]"},{"139","[email protected]","[email protected]"}};
Union_Find u = new Union_Find(line.length);
for(int i = 0;i<line.length;i++) {
for(int j = 0;j<line[i].length;j++) {
if(i == 0) {
HashMap<String,Integer> t = new HashMap<String,Integer>();
arr[i] = 1;
t.put(line[i][j], i);
list.add(t);
}else {
HashMap<String,Integer> temp = list.get(j);
if(temp.containsKey(line[i][j]))
u.union(temp.get(line[i][j]), i);
else
list.get(j).put(line[i][j], i);
}
}
}
System.out.println(u.isConnected(1, 2));
}
}
Union_Find是一个并查集实现,这里略过。如果有更好的解法或者bug。欢迎留言+交流,vx:LEILE111,邮箱[email protected]