将多个集合合并成没有交集的集合。 给定一个字符串的集合,格式如:{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;
}
转载:https://www.cnblogs.com/wft1990/p/6984710.html