给定一个字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求将其中交集不为空的集合合并,要求合并完成后的集合之间无交集,例如上例应输出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
我面试的时候说的是建立邻接矩阵,然后深度遍历的方法,时间复杂度和空间复杂度都是O(n*n),估计面试官也不满意。
网上找的,相关的解答非常少,最详细,最优复杂度的应该是这个https://www.iteye.com/blog/bylijinnan-1502690,以下的题解是我参考前面的链接,详细描述整个算法过程而写的。
大致的做法如下:
1、首先给每个集合按顺序编号0,1,2,。。。
0: {aaa bbb ccc}
1: {bbb ddd}
2: {eee fff}
3: {ggg}
4: {ddd hhh}
2、创建一个map,在每个键值对中,key是一个字符串,value是一个链表,链表装的是这个字符串所在的集合里编号(一个字符串可能出现在多个集合中,其对应的链表有多个节点)。为了构建这样的一个map,需要依次遍历所有集合,对于遍历到的每一个字符串,都在它对应的链表里记录下来当前它所在的集合编号。最后map里的键值对如下:
aaa: 0
bbb: 0, 1
ccc: 0
ddd: 1, 4
eee: 2
fff: 2
ggg: 3
hhh: 4
3、最后就是考虑如何合并集合。首先设置一个长度等于集合个数的int数组,即A[5]。其中A[m]=n;代表把集合m合并到集合n里去。为了构造这个数组 ,需要遍历map中每个键值对。查看当前遍历到的字符串对应的链表里的节点数:
A[后面节点的集合编号]=第一个节点的集合编号
具体过程如下,记录了每遍历一个键值对后int数组的变化,其中int数组初始化元素全为-1,代表这个集合不需要合并到其他集合里去:
[-1, -1, -1, -1, -1]
aaa: 0
[-1, -1, -1, -1, -1]
bbb: 0, 1
[-1, 0, -1, -1, -1] //使A[1]=0
ccc: 0
[-1, 0, -1, -1, -1]
ddd: 1, 4
[-1, 0, -1, -1, 0] //使A[4]=1,但是由于A[1]=0,即集合1已经计划合并到0了,所以这里改为A[4]=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]
可见最后int数组为[-1, 0, -1, -1, 0]
,说明原本的集合1、4被合并到集合0里去了,合并后总共剩下三个集合0、2、3。
4、最后按照上面得出的int数组合并集合即可。