并查集实现Kruskal算法

来源:互联网 发布:如何评价新东方 知乎 编辑:程序博客网 时间:2024/05/01 15:18
#include<stdio.h>#include<stdlib.h>#define MAXVEX 100      //最大顶点数#define MAXSIZE 20#define OK 1#define ERROR 0typedef char VertexType;     //顶点typedef int EdgeType;   //权值#define INFINITY 65535      /*用65535来代表∞*/#define UNVISITED -1    //标记未访问#define VISITED 1   //标记未访问//并查集时用到typedef struct parTreeNode{    VertexType value;   //顶点(结点)元素    int nCount; //子树元素数目    struct parTreeNode * parent;    //父结点指针}parTreeNode;typedef struct{    int from;   //边的始点    int to; //边的终点    EdgeType weight;    //权重}Edge;  //边的结构//图的结构typedef struct{    int numVertex;  //顶点个数    int numEdge;    //边的个数    parTreeNode vexs[MAXVEX];   /*顶点表*/    int Indegree[MAXVEX];   //顶点入度    int Mark[MAXVEX];   //标记是否被访问过    EdgeType arc[MAXVEX][MAXVEX];   //边表    Edge MST[MAXVEX];   //数组MST用于保存最小生成树的边}Graph;typedef int Status;typedef Edge ElemType;  //定义为Edge类型//最小堆的存储结构typedef struct {    ElemType heapArray[MAXSIZE];    int length;}MinHeap;//返回依附于顶点的第一条边Edge FirstEdge(Graph * G,int oneVertex);//返回与preEdge有相同顶点的下一条边Edge NextEdge(Graph * G,Edge preEdge);//判断是否为边bool IsEdge(Edge oneEdge);//返回node结点的根结点parTreeNode * Find(parTreeNode * node){    parTreeNode * pointer=node;    while(pointer->parent!=NULL)    {        pointer=pointer->parent;    }    return pointer;}//判断结点i和j是否有相同的根结点bool Different(Graph * G,int i,int j){    parTreeNode * pointer_i=Find(&G->vexs[i]);  //找到结点i的根    parTreeNode * pointer_j=Find(&G->vexs[j]);  //找的结点j的根    return pointer_i!=pointer_j;    //若结点i和j的根结点不相同返回true}//合并void Union(Graph * G,int i,int j){    parTreeNode * pointer_i=Find(&G->vexs[i]);  //找到结点i的根    parTreeNode * pointer_j=Find(&G->vexs[j]);  //找的结点j的根    if(pointer_i!=pointer_j)    {        if(pointer_i->nCount>=pointer_j->nCount)        {            pointer_j->parent=pointer_i;    //把结点i设置为j的父结点            pointer_i->nCount=pointer_i->nCount+pointer_j->nCount;        }        else        {            pointer_i->parent=pointer_j;    //把结点j设置为i的父结点            pointer_j->nCount=pointer_i->nCount+pointer_j->nCount;        }    }}//初始化堆数组Status Init_heapArray(Graph * G,MinHeap * M){    for(int i=0;i<G->numVertex;i++)    {        for(Edge e=FirstEdge(G,i);IsEdge(e);e=NextEdge(G,e))        {            if(e.from<e.to) //对于无向图,防止重复插入边            {                M->heapArray[M->length]=e;                M->length++;            }        }    }    return OK;}//对最小堆初始化Status Init_MinHeap(Graph * G,MinHeap * M){    M->length=0;    Init_heapArray(G,M);    return OK;}int MinHeap_Leftchild(int pos)  //返回左孩子的下标{    return 2*pos+1;}int MinHeap_Rightchild(int pos) //返回右孩子的下标{    return 2*pos+2;}int MinHeap_Parent(int pos) //返回双亲的下标{    return (pos-1)/2;}void MinHeap_SiftDown(MinHeap * M,int left){    int i=left; //标识父结点    int j=MinHeap_Leftchild(i); //用于记录关键值较小的子结点    ElemType temp=M->heapArray[i];  //保存父结点    while(j<M->length)  //过筛    {        if((j<M->length-1)&&(M->heapArray[j].weight>M->heapArray[j+1].weight))  //若有右子结点,且小于左子结点        {            j++;    //j指向右子结点        }        if(temp.weight>M->heapArray[j].weight)  //如果父结点大于子结点的值则交换位置        {            M->heapArray[i]=M->heapArray[j];            i=j;            j=MinHeap_Leftchild(j);        }        else    //堆序性满足时则跳出        {            break;        }    }    M->heapArray[i]=temp;}void MinHeap_SiftUp(MinHeap * M,int position)   //从position开始向上调整{    int temppos=position;    ElemType temp=M->heapArray[temppos];    //记录当前元素    while((temppos>0) && (M->heapArray[MinHeap_Parent(temppos)].weight>temp.weight))    //temppos>0,结束于根结点    {        M->heapArray[temppos]=M->heapArray[MinHeap_Parent(temppos)];        temppos=MinHeap_Parent(temppos);    }    M->heapArray[temppos]=temp;}void Swap(MinHeap * M,int data1,int data2){    ElemType temp;    temp=M->heapArray[data1];    M->heapArray[data1]=M->heapArray[data2];    M->heapArray[data2]=temp;}//建立最小堆void Create_MinHeap(MinHeap * M){    for(int i=M->length/2-1;i>=0;i--)    {        MinHeap_SiftDown(M,i);    }}//删除最小堆的最小值Status MinHeap_Delete(MinHeap * M,ElemType * MinElem){    if(M->length==0)    {        printf("不能删除,堆已空!\n");        return ERROR;    }    else    {        Swap(M,0,--M->length);        if(M->length>1)        {            MinHeap_SiftDown(M,0);        }        *MinElem=M->heapArray[M->length];        return OK;    }}//初始化图void InitGraph(Graph * G,int numVert,int numEd )    //传入顶点个数,边数{    G->numVertex=numVert;    G->numEdge=numEd;    for(int i=0;i<numVert;i++)    {        G->vexs[i].nCount=1;        G->vexs[i].parent=NULL;        G->Mark[i]=UNVISITED;        G->Indegree[i]=0;        for(int j=0;j<numVert;j++)        {            G->arc[i][j]=INFINITY;            if(i==j)            {                G->arc[i][j]=0;            }        }    }    return ;}//判断是否为边bool IsEdge(Edge oneEdge){    if(oneEdge.weight>0 && oneEdge.weight!=INFINITY && oneEdge.to>=0)    {        return true;    }    else    {        return false;    }}//建立有向图的邻接矩阵void CreatGraph(Graph * G){    int i,j,k,w;    printf("请输入%d个顶点元素:\n",G->numVertex);    for(i=0;i<G->numVertex;i++)    {        scanf(" %c",&G->vexs[i].value);    }    for(k=0;k<G->numEdge;k++)    {        printf("请输入边(Vi,Vj)的下标Vi,Vj,和权重w:\n");        scanf("%d%d%d",&i,&j,&w);        G->Indegree[j]++;        G->arc[i][j]=w;    }}//返回顶点个数int VerticesNum(Graph * G){    return G->numVertex;}//返回依附于顶点的第一条边Edge FirstEdge(Graph * G,int oneVertex){    Edge firstEdge;    firstEdge.from=oneVertex;    for(int i=0;i<G->numVertex;i++)    {        if(G->arc[oneVertex][i]!=0 && G->arc[oneVertex][i]!=INFINITY)        {            firstEdge.to=i;            firstEdge.weight=G->arc[oneVertex][i];            break;        }    }    return firstEdge;}   //返回oneEdge的终点int ToVertex(Edge oneEdge){    return oneEdge.to;}//返回与preEdge有相同顶点的下一条边Edge NextEdge(Graph * G,Edge preEdge){    Edge myEdge;    myEdge.from=preEdge.from;   //边的始点与preEdge的始点相同    if(preEdge.to<G->numVertex) //如果preEdge.to+1>=G->numVertex;将不存在下一条边        for(int i=preEdge.to+1;i<G->numVertex;i++)  //找下一个arc[oneVertex][i]        {                                           //不为0的i            if(G->arc[preEdge.from][i]!=0 && G->arc[preEdge.from][i]!=INFINITY)            {                myEdge.to=i;                myEdge.weight=G->arc[preEdge.from][i];                break;            }        }        return myEdge;}//设置一条边Edge Setedge(int from,int to,int weight){    Edge edge;    edge.from=from;    edge.to=to;    edge.weight=weight;    return edge;}void Edge_to_MST(Graph * G,Edge e,int num){    G->MST[num]=e;}//打印出MST数组void Print_MST(Graph * G,int n){    for(int i=0;i<n;i++)    {        printf("elem:%c->%c   Edge:(%d,%d)  length:%d\n",G->vexs[G->MST[i].from].value,G->vexs[G->MST[i].to].value,G->MST[i].from,G->MST[i].to,G->MST[i].weight);    }    printf("\n");}void Kruskal(Graph * G,MinHeap * M){    Init_MinHeap(G,M);    Create_MinHeap(M);    int MSTtag=0;    int EquNum=G->numVertex;    //开始n个顶点分别作为一个等价类    while(EquNum>1) //当等价类的个数大于1时合并等价类    {        Edge e;        if(M->length!=0)    //堆不为空        {            MinHeap_Delete(M,&e);   //获得一条权值最小的边        }            if(M->length==0 || e.weight==INFINITY)            {                printf("不存在最小生成树!\n");                return ;            }            int from=e.from;            int to=e.to;            if(Different(G,from,to))    //边e的两个顶点不在一个等价类            {                Union(G,from,to);   //将边e的两个顶点所在的等价类合并为一个                Edge_to_MST(G,e,MSTtag++);                EquNum--;            }    }        Print_MST(G,MSTtag);}int main(){    Graph G;    MinHeap M;    int numVert,numEd;    printf("请输入顶点数和边数:\n");    scanf("%d%d",&numVert,&numEd);    InitGraph(&G,numVert,numEd );    CreatGraph(&G);    Kruskal(&G,&M);    return 0;}

这里写图片描述这里写图片描述
这里写图片描述

0 0
原创粉丝点击