面試算法題:將多個集合合併成沒有交集的集合

題目

給定一個字符串的集合,格式如:{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數組合並集合即可。

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