最小生成树的两种经典算法--prim算法和kruskal算法

  • 一个连通图的生成树是图的一个极小连通子图,它包含所有顶点,但只有足以构成树的n-1条边
  • 这意味着对生成树来说,砍去它的任何一条边,就会使生成树变成非连通图,若给他增加一条边就会形成一条回路
  • 最小生成树:权值最小的那颗生成树叫~
  • 最小生成树的性质:

    1. 最小生成树并不唯一,准确的来说是最小生成树的树形并不唯一
    2. 最小生成树的权值之和唯一,并且是最小的
    3. 最小生成树的边数=顶点数-1
  • 求最小生成树有两种经典算法:普里姆算法(prim)克鲁斯卡尔(kruskal)算法

普里姆算法求最小生成树代码(粘贴即能跑)

#include <iostream>
#include<stdlib.h>
#define maxSize 100
#define infinity 65535
using namespace std;
typedef struct{
    char vnode[maxSize];
    int edge[maxSize][maxSize];
    int n,e;
}MGraph; 

void createMGraph(MGraph &G){//邻接矩阵构造图 
    int i,j,n,e,k,w;
    cout<< "请输入图的顶点数和边数"<<endl;
    cin>> G.n >> G.e;
    n=G.n;
    e=G.e;
    cout<< "请输入顶点"<<endl;
    for(i=0;i<n;i++){
        cin>> G.vnode[i]; 
    }
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            G.edge[i][j]=infinity;
        }
    }
    cout<< "请输入边的下标i,j"<<endl;
    for(k=0;k<e;k++){
        scanf("%d %d %d",&i,&j,&w);
        G.edge[i][j]=w;
        G.edge[j][i]=G.edge[i][j];
    } 
}

void minSpanTree_prim(MGraph G){//prim算法求最小生成树 
    int n,e,i,j,min,k,t;
    n=G.n;
    int lowcost[maxSize];//为0表示顶点加入最小生成树,其他存放边的权值 
    int adjvex[maxSize];//这个存放顶点下标,标明顶点是新增顶点,还是之前遍历过的顶点 
    lowcost[0]=0;//把首个顶点(下标为0的顶点)加入到最小生成树 
    adjvex[0]=0;//下标为0 
    for(i=1;i<n;i++){//循环0下标顶点与其他顶点的连接情况 (不从0开始是因为0 0表示自己和自己的环) 
        lowcost[i]=G.edge[0][i];//把0下标顶点和其他顶点组成的边的权值存放到lowcost数组中 
        adjvex[i]=0;//当前lowcost数组中的边的权值的起始顶点全部都是下标为0的顶点,而结束顶点则是序号为lowcost数组下标的顶点 
    }
    cout<<"最小生成树为:"<<endl; 
    for(t=1;t<n;t++){//循环所有顶点,构造最小生成树 
        min=infinity;//初始化min,刚开始为一个极大值 
        j=1;k=0;
        while(j<n){//遍历lowcost数组 
            if(lowcost[j]!=0&&lowcost[j]<min){//除去已加入最小生成树的顶点 
                min=lowcost[j];//找出lowcost数组中最小的权值,并赋值给min 
                k=j;//记录最小权值的下标 (这个k其实就是权值最小的那条边的结束顶点) 
            }
            j++;
        }
        printf("(%d,%d)\n",adjvex[k],k);//打印权值最小的那条边的起始顶点和结束顶点 
        lowcost[k]=0;//把k下标的顶点加入到最小生成树 
        for(i=1;i<n;i++){//遍历顶点 
            if(lowcost[i]!=0&&G.edge[k][i]<lowcost[i]){//要除去已加入最小生成树的顶点 
                lowcost[i]=G.edge[k][i];//在k结点与其他顶点邻接的权值和lowcost数组中取较小的一方更新lowcost 
                adjvex[i]=k;//记录较小权值的边的起始顶点下标 
            }
        }
    }
}
int main(int argc, char** argv) {
    MGraph G;
    createMGraph(G);
    minSpanTree_prim(G);
    return 0;
}
    /*
    示例输入:
        顶点数和边数: 9 15
        输入顶点:   0 1 2 3 4 5 6 7 8
        输入顶点下标和权值: 
                4 7 7
                2 8 8
                0 1 10
                0 5 11
                1 8 12
                3 7 16
                1 6 16
                5 6 17
                1 2 18
                6 7 19
                3 4 20
                3 8 21
                2 3 22
                3 6 24
                4 5 26
    */

克鲁斯卡尔算法构造最小生成树代码:

#include <iostream>
#include<algorithm>
#include<stdlib.h>
#define maxSize 100
#define infinity 65535
using namespace std;
typedef struct{//邻接矩阵构造的图结点 
    char vnode[maxSize];
    int edge[maxSize][maxSize];
    int n,e;
}MGraph; 

typedef struct{//边集结点(存放边的顶点下标和边的权重) 
    int start;//边起点 
    int end;//边终点 
    int w;//边权值 
}Road; 
Road road[maxSize];//边集数组
int parent[maxSize];
int getRoot(int i){//此函数用于找到下标为i的顶点在生成树中的父节点 (并查集) 
    while(parent[i]!=i){  
        i=parent[i];
    }
    return i;
}

bool compare(Road x,Road y){//自定义结构体比较方式,按结构体中的权值升序排 
    return x.w<y.w;
}

void createMGraph(MGraph &G){//创建图 
    int i,j,n,e,k,w;
    cout<< "请输入图的顶点数和边数"<<endl;
    cin>> G.n >> G.e;
    n=G.n;
    e=G.e;
    cout<< "请输入顶点"<<endl;
    for(i=0;i<n;i++){
        cin>> G.vnode[i]; 
    }
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            G.edge[i][j]=infinity;
        }
    }
    cout<< "请输入边的下标i,j"<<endl;
    for(k=0;k<e;k++){
        scanf("%d %d %d",&i,&j,&w);
        G.edge[i][j]=w;
        road[k].start=i;//创建的时候就给边集数组赋值
        road[k].end=j;
        road[k].w=w;
        G.edge[j][i]=G.edge[i][j];//根据无向图邻接矩阵的对称性赋值
    } 
}

int kruskal(MGraph G){//克鲁斯卡尔算法 
    int a,b,sum=0;
    int e=G.e;
    for(int i=0;i<G.e;i++){//初始化根结点下标数组 
        parent[i]=i;//为存放根节点下标数组赋初值,自身作为自己的根节点 
    } 
    sort(road,road+e,compare);//按边集数组中的权值由小到大排序 
    for(int i=0;i<e;i++){//遍历已经排好序的边 
        a=getRoot(road[i].start);//获取开始顶点在生成树中的父节点
        b=getRoot(road[i].end);//获取终结顶点在生成树中的父节点
        if(a!=b){//当ab不等说明两者不是同一个父节点,不会构成环 
            parent[a]=b;//把b点作为孩子加在a的后面 
            printf("(%d,%d)\n",road[i].start,road[i].end);//打印构成最小生成树的边
            sum+=road[i].w;//最小生成树的权值总和
        }
    }
    printf("sum=%d\n",sum);
    return sum;
}
int main(int argc, char** argv) {
    MGraph G;
    createMGraph(G);
    kruskal(G);
    return 0;
}
/*
    示例输入:
        顶点数和边数: 9 15
        输入顶点:   0 1 2 3 4 5 6 7 8
        输入顶点下标和权值: 
                4 7 7
                2 8 8
                0 1 10
                0 5 11
                1 8 12
                3 7 16
                1 6 16
                5 6 17
                1 2 18
                6 7 19
                3 4 20
                3 8 21
                2 3 22
                3 6 24
                4 5 26
    */
  • 9
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值