面试算法题:将多个集合合并成没有交集的集合

题目

给定一个字符串的集合,格式如:{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中每个键值对。查看当前遍历到的字符串对应的链表里的节点数:

  • 如果链表节点数为1,说明这个字符串只在一个集合里出现过,那么它不会引起集合的合并,直接考虑下一个字符串。
  • 如果链表节点数大于1,说明这个字符串在多个集合里出现过,它会引起集合的合并。此时要做的就是,把链表后面的节点的集合全部合并到第一个节点的集合里去,具体来说就是设置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数组合并集合即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章