將多個集合合並成沒有交集的集合。
給定一個字符串的集合,格式如:{aaa bbb ccc}, {bbb ddd},{eee fff},{ggg},{ddd hhh}要求將其中交集不為空的集合合並,要求合並完成后的集合之間無交集,例如上例應輸出{aaa bbb ccc ddd hhh},{eee fff}, {ggg}。
(1)請描述你解決這個問題的思路;
(2)請給出主要的處理流程,算法,以及算法的復雜度
(3)請描述可能的改進。
采用並查集。(關於並查集,上篇博文講了)
首先所有的字符串都在單獨的並查集中。然后依掃描每個集合,順序合並將兩個相鄰元素合並。例如,對於,首先查看aaa和bbb是否在同一個並查集中,如果不在,那么把它們所在的並查集合並,然后再看bbb和ccc是否在同一個並查集中,如果不在,那么也把 它們所在的並查集合並。接下來再掃描其他的集合,當所有的集合都掃描完了,並查集代表的集合便是所求。復雜度應該是O(NlgN)的。改進的話,首先可以 記錄每個節點的根結點,改進查詢。合並的時候,可以把大的和小的進行合,這樣也減少復雜度。
#include <stdio.h> #include <stdlib.h> #define MAX 26 //將給定的字符串的集合轉化為如下的關系“aaa”編號為1,以此類推。。。。 int relation[6][2] = { {1,2},//{"aaa","bbb"} {1,3},//{"aaa","ccc"} {2,4}, {5,6}, {4,8}, {7,7}//{ggg} }; //(之所以這么復雜去實現,主要是為了輸出ggg,目前使用並查集沒有更好的辦法) //找主根(一開始初始化為-1,如果A[x]<0,首先 //給其根節點賦值為本身並返回,其次其根節點為本身的,返回其本身。) //此函數主要目的是在集合合並處使用 int find_root(int A[], int x) { //結合調用的for循環i=0~6;故只有出現的字母才會出現自己的根節點是自己,沒有出現的字母根節點仍然是-1;(為了以后再輸出時方便,加以控制) if(A[x]<0) { A[x]=x; return x; } else if(A[x]==x) return x; else return find_root(A, A[x]); } //(此函數主要是在最后結果輸出時使用 ) //返回根節點 int findroot(int A[],int x) { if(A[x]==x||A[x]==-1) return A[x]; else return findroot(A, A[x]); } int main(int argc, char *argv[]) { int i; int root1; int root2; int A[MAX];//根節點的存儲 //一開始根節點的數組里面的值初始化為-1 for(i=0;i<26;i++) A[i] = -1; //遍歷relation二維數組來實現集合的合並 for(i=0;i<6;i++) { root1 = find_root(A, relation[i][0]); root2 = find_root(A, relation[i][1]); if(root1!=root2)//集合根節點的合並 (此處還可以優化?) A[root1]=root2; } //結果的輸出 int flag[26]={0}; for(i=1;i<26;i++) { if(flag[i]) continue; int mark=findroot(A,i);//為了輸出找根節點 之前是A[i] by felix //根節點為-1的不考慮 if(mark!=-1) { flag[i]=1; printf("%c%c%c\t",i+'a'-1,i+'a'-1,i+'a'-1); for(int j=i+1;j<26;j++) { if(flag[j]) continue; int marks=findroot(A,A[j]); if(marks==mark) { flag[j]=1; printf("%c%c%c\t",j+'a'-1,j+'a'-1,j+'a'-1); } } puts(""); } } system("PAUSE"); return 0; }
方法二:使用hash_table方法
http://www.cnblogs.com/ttltry-air/archive/2012/08/14/2638437.html