将多个集合合并成没有交集的集合。
给定一个字符串的集合,格式如:{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