并查集---判断图中是否存在环

来源:互联网 发布:网络神曲排行榜前十名 编辑:程序博客网 时间:2024/05/17 04:00

算法描述:

并查集(union-find sets)是一种简单的用途广泛的集合. 并查集是若干个不相交集合,能够实现较快的合并和判断元素所在集合的操作,应用很多,如其求无向图的连通分量个数最小公共祖先带限制的作业排序,还有最完美的应用:实现Kruskar算法求最小生成树

算法实现:

//并查集判断是否存在环#include <iostream>#include <stdlib.h>#include <string.h>using namespace std;//图中的边struct Edge{    int start;//边的起点    int end;//边的终点};//图结构体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->E条边的图    graph->edge = (struct Edge*) malloc(graph->E*sizeof(struct Edge));    return graph;};//查找元素i所在集合(根节点)int find(int parent[], int i){    //一维数组parent[] 来记录子集合    if(parent[i]==-1)    {//i的根节点是其自身        return i;    }    //查找i的根节点所在集合    return find(parent, parent[i]);}//合并集合void join(int parent[], int x, int y){    int xset=find(parent,x);    int yset=find(parent,y);    if(xset != yset)    {        parent[xset]=yset;    }}//检测是否有环int isCycle(struct Graph* graph){    int *parent = (int*) malloc(graph->V * sizeof(int));    //初始化所有集合(每个节点)    //parent[n] 的每个元素都为-1,共有n个子集合,表示集合只有当前顶点一个元素    memset(parent, -1, sizeof(int) * graph->V);    //遍历所有边    for(int i=0;i<graph->E;++i)    {        int x = find(parent, graph->edge[i].start);        int y = find(parent, graph->edge[i].end);        if(x==y)        {//如果在一个集合就找到了环            return 1;        }        join(parent, x, y);    }    return 0;}int main(){    /* 创建图         0         | \         |   \         1----2 */    struct Graph* graph = createGraph(3,3);    // 添加边 0-1    graph->edge[0].start=0;    graph->edge[0].end=1;    // 添加边 1-2    graph->edge[1].start=1;    graph->edge[1].end=2;    // 添加边 0-2    graph->edge[2].start=0;    graph->edge[2].end=2;    if (isCycle(graph))    {        cout<<"graph contains cycle"<<endl;    }    else    {        cout<<"graph doesn't contain cycle"<<endl;    }    return 0;}

九度1024 畅通工程

//使用并查集实现了kruskal算法求最小花费树#include <iostream>#include<stdio.h>#include<stdlib.h>using namespace std;typedef struct node{    int start;    int end;    int cost;}Node;int father[101];int rank[101];void Make_Set(int M){    for(int i=1;i<=M;i++)//村庄从1开始编号    {        father[i]=i;        rank[i]=0;    }}int Find_Set(int x){    if(x != father[x])    {        father[x]=Find_Set(father[x]);    }    return father[x];}int Union(int x,int y){    x=Find_Set(x);    y=Find_Set(y);    if(x==y) return 0;//若没有合并,则返回0    if(rank[x]>rank[y])    {        father[y]=x;        rank[x] += rank[y];    }    else    {        if(rank[x]==rank[y])        {            ++rank[y];        }        father[x]=y;    }    return 1;//若进行了合并,则返回1}int cmp(const void *p, const void *q){    Node * p1=(Node *)p;    Node * q1=(Node *)q;    return p1->cost - q1->cost;//从小到大}int main(){    int N,M;    int ans;    int cnt;    Node road[5000];    while(cin>>N>>M&&N)    {        for(int i=0;i<N;i++)        {            scanf("%d%d%d",&road[i].start,&road[i].end,&road[i].cost);        }        qsort(road,N,sizeof(Node),cmp);//将N条道路的花费值从小到大排序        Make_Set(M);        ans=0;        cnt=0;        for(int i=0;i<N;i++)        {            if(cnt==M-1)//按照树的定义,M个点有M-1条连线                break;            if(Union(road[i].start,road[i].end))            {                ++cnt;                ans += road[i].cost;            }        }        if(cnt==M-1)//实现了kruskal算法求最小花费树        {            printf("%d\n",ans);        }        else        {            printf("?\n");        }    }    return 0;}
0 0