并查集(1)-判断无向图是否存在环

并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。集就是让每个元素构成一个单元素的集合,也就是按一定顺序将属于同一组的元素所在的集合合并。

  • Find:确定元素属于哪一个子集。它可以被用来确定两个元素是否属于同一子集合。
  • Union:将两个子集合并成同一个集合。

其实判断一个图是否存在环已经有相应的算法,此文用并查集来判断一个图是否有环。

我们可以用一个一维数组parent[] 来记录子集合。

看下面这个图:

0
|  |    1-----2

对每一条边的两个顶点加入集合,发现两个相同的顶点在一个子集合中,就说明存在环。

初始化:parent[n] 的每个元素都为-1,共有n个子集合,表示集合只有当前顶点一个元素。

0 1 2
-1 -1 -1

然后逐个处理每条边。

边0-1:我们找到两个子集合 0 和1,因为他们在不同的子集合,现在需要合并他们(Union). 把其中一个子集合作为对方的父集合.

0   1   2    <----- 1 成为 0 的 父集合 (1 现在代表集合 {0, 1})
1  -1  -1

边0-2:1属于属于子集合1,2属于子集合2,因此合并他们。

0   1   2    <----- 2 作为 1的父集合 (2 现在代表集合 {0, 1, 2})
1   2  -1

 边0-2: 0是在子集和2和2也是在子集合2, 因为 0->1->2 // 1 是0 父集合 并且  2 是1的父集合 。因此,找到了环。

代码:

// 用并查集判断是否存在环
#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;
}

并查集(1)-判断无向图是否存在环,布布扣,bubuko.com

时间: 2024-10-05 06:09:10

并查集(1)-判断无向图是否存在环的相关文章

HDU 3038 How Many Answers Are Wrong (带权并查集+区间判断)

题意:给你长度为n的区间,m个询问:a,b,c,问这m个问题有多少个是错误的(矛盾). 10 5 1 10 100 7 10 28 1 3 32 4 6 41 6 6 1 由6->6=1,  4->6=41 知4->5=40; 同理 由1->10=100,7->10=28 知1->7=72; 又由1->3=32,4-6=41 知1->7=73,与上面矛盾: 所以答案为1: #include<cstdio> #include<stdlib.h

HDU 1272 小希的迷宫(并查集,判断是否成环)

小希的迷宫 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 41583    Accepted Submission(s): 12822 Problem Description 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该

贝壳找房魔法师顾问[并查集+DAG判断]

题目链接[https://nanti.jisuanke.com/t/27647] //计蒜客2018复赛D题,想简单了. 题解: 题目是中文的,不再赘述. 题解: 分为三种情况:1.两个字符串都不能变:这种情况最简单,直接暴力判断两个支付穿是否相同即可. 2.两个字符串都能变:把上下对应位置不同的点连无向边(因为都可以改变),建立了一个深林,这里用并查集实现,顺便维护每个联块的大小,对于每个联通块来说,要把这些点都变成一样的,最少要变换(size-1)次,即选出一个点,把其他的点都变成被选中的点

HDU 5222 ——Exploration——————【并查集+拓扑排序判有向环】

Exploration Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 194    Accepted Submission(s): 63 Problem Description Miceren likes exploration and he found a huge labyrinth underground! This la

HDU-1272 小希的迷宫 (并查集、判断图是否为树)

Description 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房 间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走 了回头路).小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路.比如下面的例子,前两个是符合条件的,但是最后

HDU-1232 畅通工程 (并查集、判断图中树的棵数)

Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇.省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可).问最少还需要建设多少条道路? Input 测试输入包含若干测试用例.每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M:随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号.为简单起见,城镇从1到N编号. 注意

HDOJ1272(并查集,判断是否为树)

0 0 Yes 1 1 0 0 Yes 1 2 2 1 0 0 No //自回路不算一条边的! 居然有 0 0 这样的测试数据 #include<iostream> #include<cstdio> #include<cstring> #include<set> using namespace std; set<int> ans; set<int> member; const int SIZE=100000+16; int par[S

cf290-2015-2-3总结与反思(dfs判断无向图是否有环)

bool dfs(int i,int pre) { visit[i]=true; for(int j=1;j<=v;j++) if(g[i][j]) { if(!visit[j]) return dfs(j,i); else if(j!=pre) //如果访问过,且不是其父节点,那么就构成环 return false; } } 方法:从一个顶点出发深度优先遍历可遍历所有结点,并且没有环或只有n-1条边. 若判断有环:可以在遍历时记住父结点,v的子结点w已被访问,且不是结点v的父结点,则存在环.

并查集(2)-按秩合并和路径压缩

在上面一讲是并查集(1)-判断无向图是否存在环. 我们使用了并查集的两个操作: union() 和 find() // find 的原始实现 int find(int parent[], int i) { if (parent[i] == -1) return i; return find(parent, parent[i]); } // union()的原始实现 void Union(int parent[], int x, int y) { int xset = find(parent, x