并查集实现Kruskal算法

#include<stdio.h>
#include<stdlib.h>
#define MAXVEX 100      //最大顶点数
#define MAXSIZE 20
#define OK 1
#define ERROR 0
typedef 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值