内容:
并查集用来判断元素是否在同一集合中,如果利用树的结构表示,直观意义则是每个元素只记录父节点
parent数组用于记录该信息,初始化都为-1
对于两个点(x),(y)属于的集合,将(x)归并到(y)中。
注意:
1. 已经为一个集合避免成环
2. 闭元关系要明确
3.多利用位运算
关系并查集 poj1182 食物链
经典题目了,relation里面存放的都是结点与根节点的关系,所以合并的时候反了一下关系数-x
0为初始,一般设计为同类。
这里假设合并x ,y 。xx,yy分别为其父点。则已知:x->xx,x->y,y->yy,将xx合并到yy上,此时需要将这几个关系合并,且需要xx->x的关系用关系数-x
在find中只有父节点更新完得到新关系才能再修改。
应用一:
int find(int *parent,int k)
{
if(parent[k]==-1)
return k;
int t=parent[k];
parent[k]=find(parent,parent[k]);//路径压缩
relate[k]=(relate[k]+relate[t])%2;//这里relate记录的是与根节点的关系2为关系闭元具体的可以看poj 1182 食物链
return parent[k];
}
/*
要注意当合并两个集合
应用二:
// 用并查集判断是否存在环
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 图中的边
struct Edge
{
int src, dest;
};
// 图结构体
struct Graph
{
// V-> 顶点个数, E-> 边的个数
int V, E;
// 每个图就是 边的集合
struct Edge* edge;
};
// 创建一个图
struct Graph* createGraph(int V, int E)
{
struct Graph* graph = (struct Graph*) malloc( sizeof(struct Graph) );
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*) malloc( graph->E * sizeof( struct Edge ) );
return graph;
}
// 查找元素i 所在的集合( 根 )
int find(int parent[], int i)
{
if (parent[i] == -1)
return i;
return find(parent, parent[i]);
}
// 合并两个集合
void Union(int parent[], int x, int y)
{
int xset = find(parent, x);
int yset = find(parent, y);
parent[xset] = yset;
}
// 检测环
int isCycle( struct Graph* graph )
{
int *parent = (int*) malloc( graph->V * sizeof(int) );
// 初始化所有集合
memset(parent, -1, sizeof(int) * graph->V);
// 遍历所有边
for(int i = 0; i < graph->E; ++i)
{
int x = find(parent, graph->edge[i].src);
int y = find(parent, graph->edge[i].dest);
if (x == y) //如果在一个集合,就找到了环
return 1;
Union(parent, x, y);
}
return 0;
}
// 测试
int main()
{
/* 创建一些的图
0
| \
| \
1-----2 */
struct Graph* graph = createGraph(3, 3);
// 添加边 0-1
graph->edge[0].src = 0;
graph->edge[0].dest = 1;
// 添加边 1-2
graph->edge[1].src = 1;
graph->edge[1].dest = 2;
// 添加边 0-2
graph->edge[2].src = 0;
graph->edge[2].dest = 2;
if (isCycle(graph))
printf( "Graph contains cycle" );
else
printf( "Graph doesn't contain cycle" );
return 0;
}