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