最小生成树kruskal算法适合稀疏图(网络整理)8.1.3

上一篇实现了prim算法,适合稠密图,这次来实现另一种算法KRUSKAL算法

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#define MAXSIZE 100
typedef int Weight;
typedef char Datatype; 
typedef struct AdjVNode{
Weight weight;
int adjv;
struct AdjVNode *Next;
}*PtrToAdjVNode;
typedef struct Vnode{
Datatype data;
PtrToAdjVNode FirstEdge;/* 边表头指针 */  
}AdjList[MAXSIZE];
typedef struct Lnode{
int Nv;
int Ne;
AdjList G;
}*LGraph;
typedef struct Enode{
int v1;
int v2;
Weight weight;
//bool flag;//true -> false ->
}Edge;




LGraph  CreateGraph(int n){
LGraph Graph=(LGraph)malloc(sizeof(struct Lnode));
Graph->Nv=n;
Graph->Ne=0;
for(int i=0;i<n;i++){
Graph->G[i].FirstEdge=NULL;

}

return Graph;
}

void InsertEdge(LGraph Graph,const Edge *ptre){
PtrToAdjVNode Newnode=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
Newnode->weight=ptre->weight;
Newnode->adjv=ptre->v2;
Newnode->Next=Graph->G[ptre->v1].FirstEdge;
Graph->G[ptre->v1].FirstEdge=Newnode;
//若是无向边

Newnode=(PtrToAdjVNode)malloc(sizeof(struct AdjVNode));
Newnode->weight=ptre->weight;
Newnode->adjv=ptre->v1;
Newnode->Next=Graph->G[ptre->v2].FirstEdge;
Graph->G[ptre->v2].FirstEdge=Newnode;



}

LGraph  buildGraph(){
LGraph Graph;  
    Edge E;  
    int V;  
    int Nv, i;  
       
    scanf("%d", &Nv);   /* 读入顶点个数 */  
    Graph = CreateGraph(Nv); /* 初始化有Nv个顶点但没有边的图 */   
       
    scanf("%d", &(Graph->Ne));   /* 读入边数 */  
    if ( Graph->Ne != 0 ) { /* 如果有边 */   
         
        /* 读入边,格式为"起点 终点 权重",插入邻接矩阵 */  
        for (i=0; i<Graph->Ne; i++) {  
            scanf("%d %d %d", &E.v1, &E.v2, &E.weight);   
            /* 注意:如果权重不是整型,Weight的读入格式要改 */  
            InsertEdge( Graph, &E );  
        } 
    } 

/* 如果顶点有数据的话,读入数据 */  
    for (V=0; V<Graph->Nv; V++)   
        scanf(" %c", &(Graph->G[V].data));  
   
    return Graph;  


}

/*-------------------- 边的最小堆定义 --------------------*/
typedef Edge Ele;
const Ele ERROR={-1,-1,-1};
typedef struct heap{
Ele *data;
int size;
int maxsize;
}*MinHeap;
MinHeap createMinHeap(int n){
MinHeap minheap=(MinHeap)malloc(sizeof(struct heap));
minheap->data=(Ele*)malloc(sizeof(Ele)*(n+1));
minheap->size=0;
minheap->maxsize=n;
return minheap;

}
bool isfull(MinHeap heap){


    return heap->size>=heap->maxsize;
}
bool isempty(MinHeap heap){
if(heap==NULL)
heap->size =5;
    return heap->size<=0;
}

void InsertMinHeap(MinHeap heap,Ele e){
if(isfull(heap))
    return;
heap->size++;
int i;
for(i=heap->size;i>1&&e.weight<heap->data[i/2].weight;i/=2)
heap->data[i]=heap->data[i/2];
heap->data[i]=e;

}

Ele DelMinHeap(MinHeap heap){
if(isempty(heap))
return ERROR;
Ele min=heap->data[1];
Ele tmp=heap->data[heap->size--];
int i=0,childmin;
for(i=1;2*i<=heap->size;i=childmin)
{
childmin=2*i;
if(childmin!=heap->size&&heap->data[childmin].weight>heap->data[childmin+1].weight)
childmin++;
if(tmp.weight<=heap->data[childmin].weight)break;
 heap->data[i]=heap->data[childmin];   
}
heap->data[i]=tmp;

return min;
}

void PercDown(MinHeap heap, int p){
 
 Ele tmp = heap->data[p]; /* 取出根结点存放的值 */
    int i=p,childmin;
for(;2*i<=heap->size;i=childmin)
{
childmin=2*i;
if(childmin!=heap->size&&heap->data[childmin].weight>heap->data[childmin+1].weight)
childmin++;
if(tmp.weight<=heap->data[childmin].weight)break;
 heap->data[i]=heap->data[childmin];   
}
heap->data[i]=tmp;


}
MinHeap buildMinHeap(Ele e[],int n){
MinHeap heap=createMinHeap(n);
heap->size=n;
for(int i=0;i<n;i++)
heap->data[i]=e[i];

for(int i=n/2;i>0;i--)
    PercDown(heap,i);

return heap;

}
/*-------------------- 边的最小堆定义end --------------------*/
/*-------------------- 顶点并查集定义 --------------------*/
typedef int ElementType; /* 默认元素可以用非负整数表示 */
typedef int SetName;     /* 默认用根结点的下标作为集合名称 */
typedef ElementType SetType[MAXSIZE]; /* 假设集合元素下标从0开始 */
 
void InitializeVSet( SetType S, int N )
{ /* 初始化并查集 */
    int X;
 
    for ( X=0; X<N; X++ ) S[X] = -1;
}
 
void Union( SetType S, SetName Root1, SetName Root2 )
{ /* 这里默认Root1和Root2是不同集合的根结点 */
    /* 保证小集合并入大集合 */
    //if ( S[Root2] < S[Root1] ) { /* 如果集合2比较大 */
      //  S[Root2] += S[Root1];     /* 集合1并入集合2  */
        //S[Root1] = Root2;
    //}
    //else {                         /* 如果集合1比较大 */
      //  S[Root1] += S[Root2];     /* 集合2并入集合1  */
        //S[Root2] = Root1;
    //}
S[Root2]=Root1;
}
 
SetName Find( SetType S, ElementType X )
{ /* 默认集合元素全部初始化为-1 */
    if ( S[X] < 0 ) /* 找到集合的根 */
        return X;
    else
        return Find( S, S[X] ); /* 路径压缩 */
}
 
bool CheckCycle( SetType VSet, int V1, int V2 )
{ /* 检查连接V1和V2的边是否在现有的最小生成树子集中构成回路 */
    int Root1, Root2;
 
    Root1 = Find( VSet, V1 ); /* 得到V1所属的连通集名称 */
    Root2 = Find( VSet, V2 ); /* 得到V2所属的连通集名称 */
 
    if( Root1==Root2 ) /* 若V1和V2已经连通,则该边不能要 */
        return false;
    else { /* 否则该边可以被收集,同时将V1和V2并入同一连通集 */
        Union( VSet, Root1, Root2 );
        return true;
    }
}


MinHeap InitializeESet(LGraph Graph){
    PtrToAdjVNode W;
    Edge edge;
MinHeap edgeheap=createMinHeap(Graph->Ne);
for (int V=0; V<Graph->Nv; V++ )
        for ( W=Graph->G[V].FirstEdge; W; W=W->Next )
            if ( V<W->adjv) { /* 避免重复录入无向图的边,只收V1<V2的边 */
                edge.v1 = V;
                edge.v2 = W->adjv;
                edge.weight=W->weight;
                //printf("abc %d\n",edge.weight );
                InsertMinHeap(edgeheap,edge);
            }
return edgeheap;
}

Edge GetEdge( MinHeap edgeheap){

    return DelMinHeap(edgeheap);
}


/*-------------------- 并查集定义结束 --------------------*/
    int Kruskal( LGraph Graph, LGraph MST )
    { /* 将最小生成树保存为邻接表存储的图MST,返回最小权重和 */
        Weight TotalWeight;
        int ECount;
        SetType VSet; /* 顶点并查集 */
        MinHeap edgeheap=NULL;    /* 边的最小堆 */
       // printf("%p\n",edgeheap );exit(0);
       Edge NextEdge;
        InitializeVSet( VSet, Graph->Nv ); /* 初始化顶点并查集 */
       
        edgeheap=InitializeESet( Graph ); /* 初始化边的最小堆 */

        /* 创建包含所有顶点但没有边的图。注意用邻接表版本 */
        MST = CreateGraph(Graph->Nv);
        TotalWeight = 0; /* 初始化权重和     */
        ECount = 0;      /* 初始化收录的边数 */
     
       
        while ( ECount < Graph->Nv-1 ) {  /* 当收集的边不足以构成树时 */
            NextEdge = GetEdge( edgeheap ); /* 从边的最小堆de到最小边 */
        //printf("%d\n", NextEdge.weight);
            if (NextEdge.v1==-1) /* 边集已空 */
                break;

            /* 如果该边的加入不构成回路,即两端结点不属于同一连通集 */
            if ( CheckCycle( VSet, NextEdge.v1, NextEdge.v2 )==true ) {
                /* 将该边插入MST */
                InsertEdge( MST, &NextEdge);
                printf("%d<->%d weight:%d collected\n",NextEdge.v1,NextEdge.v2,NextEdge.weight);
                TotalWeight += NextEdge.weight; /* 累计权重 */
                ECount++; /* 生成树中边数加1 */
            }
        }
        if ( ECount < Graph->Nv-1 )
            TotalWeight = -1; /* 设置错误标记,表示生成树不存在 */
     
        return TotalWeight;
    }







int main(){

LGraph Graph=buildGraph();
LGraph MST;
printf("-------------------------------------------\n");
printf("TotalWeight %d",Kruskal(Graph,MST));

    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值