第七章 图(三)图算法的应用

@第七章 图(三)
图算法的应用
最小生成树 :普里姆算法「Prim」、克鲁斯卡尔算法「Kruskal」

一、普里姆算法实现最小生成树

---------------------------------------------------------------------------------------------------------------------
图片名称 图片名称
图1、图2
图片名称
图3
首先主程序构造了如图2所示的无向网,然后调用MiniSpanTree_PRIM(),由顶点V1开始,求该网的最小生成树。最小生成树顶点集最初只有V1,其中用到了辅助数组 closedge[]。closedge[i].lowcost 是最小生成树顶点集中的顶点到 i 点的最小权值。若i点属于最小生成树,则closedge[i].lowcost=0。closedge[i].adjvex 是最小生成树顶点集U中到i点为最小权值的那个顶点的序号。图 3(a)显示了 closedge[]的初态。这时U中只有 V1,所以closedge[i].adjvex都是 V1,closedge[i].lowcost是V1 到顶点i的权值。closedge[0].lowcost=0,说明 V1 已并入U了。在closedge[].lowcost 中找最小正数,closedge[2].lowcost=1,是最小正数。令 k=2,将 V3 并入U,令closedge[2].lowcost=0),输出边(V1—V3)。因为 V3到V2、V5和V6的权值小于V1到它们的权值,故将它们的closedge[].lowcost替换为V3到它们的权值;将它们的 closedge[].adjvex 替换为V3,如图3(b)所示。重复这个过程,依次如图3(c)、(d)和(e)所示。最后,closedge[]包含了最小生成树中每一条边的信息。
---------------------------------------------------------------------------------------------------------------------
//邻接矩阵实现普里姆算法
#include<iostream>
#include<iomanip>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<malloc/malloc.h>
using namespace std;
#define MaxVertexNum 100
typedef int EdgeType;
typedef char VertexType[10];
typedef struct
{
    VertexType vertex[MaxVertexNum];            //顶点表
    EdgeType edges[MaxVertexNum][MaxVertexNum]; //邻接矩阵,即边表
    int n,e;        //顶点数和边数
}MGraph;
typedef struct
{
    VertexType adjvex;
    int lowcost;
}miniside[MaxVertexNum];
int LocateVex(MGraph G,VertexType u)
{
    int i;
    for(i=0;i<G.n;++i)
        if(strcmp(u,G.vertex[i])==0)
            return i;
    return -1;
}
void CreateMGraphAN(MGraph &G)
{
    int i,j,k,w;
    VertexType vi,vj;
    cout<<"请输入顶点数和边数(输入格式为:顶点数,边数):"<<endl;
    cin>>G.n>>G.e;
    cout<<"请输入顶点信息(输入格式为:顶点号<CR>):"<<endl;
    for(i=0;i<G.n;i++)
        cin>>G.vertex[i];
    for(i=0;i<G.n;i++)
        for(j=0;j<G.n;j++)
            G.edges[i][j]=0;
   cout<<"请输入"<<G.e<<"条边对应的两个顶点的序号及权值(输入格式为:顶点1,顶点2,权值)";
    for(k=0;k<G.e;k++)
    {
        cout<<"顶点1,顶点2,权值:"<<endl;
        cin>>vi>>vj>>w;
        i=LocateVex(G,vi);
        j=LocateVex(G,vj);
        G.edges[i][j]=w;
        G.edges[j][i]=w;
    }   
    
}//CreateMGraphAN
int minimum(miniside S,MGraph G)
{ //求S的最小值,并返回其在S中的序号
    int i=0,j,k,min;
    while(!S[i].lowcost) //找第一个值不为0的S[i].lowcost的序号
        i++;
    min=S[i].lowcost; //min标记第一个不为0的值
    k=i;//k表示该值的序号
    for(j=i+1;j<G.n;j++) //继续向后找
        if(S[j].lowcost>0 && S[j].lowcost < min)//找到更小的新的正值
            {
                min=S[j].lowcost;//min标记此正值
                k=j;//k标记此正值的序号
            }
        return k; //返回当前最小正值在S中的序号
}
void Prim(MGraph G,VertexType u)
{ //用prim算法从顶点u出发构造网G的最小生成树T,输出T的各边
    int i,j,k;
    miniside S;
    k=LocateVex(G,u); //顶点u的序号
    for(j=0;j<G.n;++j) //辅助数组初始化
        if(j!=k) 
        {
            strcpy(S[j].adjvex,u);//顶点u拷贝到S[j].adjvex
            if(G.edges[k][j]==0)
                S[j].lowcost=65535;
            else
                S[j].lowcost=G.edges[k][j]; //顶点u到该点的权值
        }
    S[k].lowcost=0; //初始,U={u}.U中的顶点到集合U的权值为0
    cout<<"最小代价生成树的各条边为:"<<endl;
    for(i=1;i<G.n;++i) //选择其余G.n-1个顶点
    {
        k=minimum(S,G); //求出最小生成树的下一个结点:第k个结点
        cout<<S[k].adjvex<<"->"<<G.vertex[k]<<endl; //输出最小生成树的边
        S[k].lowcost=0; //将第k个顶点并入U集
        for(j=0;j<G.n;++j)
            if(G.edges[k][j] < S[j].lowcost && G.edges[k][j]!=0)
            { //新顶点并入U集后重新选择最小边
                strcpy(S[j].adjvex,G.vertex[k]);
                S[j].lowcost=G.edges[k][j];
            }
    }
} //prim
void print(MGraph G)
{
    int i,j;
    for(i=0;i<G.n;i++)
    {   
        for(j=0;j<G.n;j++)
            cout<<setw(4)<<G.edges[i][j];   
        cout<<endl;
    }
}
int main()
{
    MGraph G;
    CreateMGraphAN(G);
    cout<<"输出图的邻接矩阵:"<<endl;
    print(G);
    Prim(G,G.vertex[0]);
}

输出结果

请输入顶点数和边数(输入格式为:顶点数,边数):
5 8
请输入顶点信息(输入格式为:顶点号<CR>):
v1
v2
v3
v4
v5
请输入8条边对应的两个顶点的序号及权值(输入格式为:顶点1,顶点2,权值)顶点1,顶点2,权值:
v1 v2 4
顶点1,顶点2,权值:
v1 v3 1
顶点1,顶点2,权值:
v1 v4 5
顶点1,顶点2,权值:
v2 v3 4
顶点1,顶点2,权值:
v2 v5 2
顶点1,顶点2,权值:
v3 v4 4
顶点1,顶点2,权值:
v3 v5 3
顶点1,顶点2,权值:
v4 v5 5
输出图的邻接矩阵:
   0   4   1   5   0
   4   0   4   0   2
   1   4   0   4   3
   5   0   4   0   5
   0   2   3   5   0
最小代价生成树的各条边为:
v1->v3
v3->v5
v5->v2
v3->v4

二、克鲁斯卡尔算法

#include<iostream>
#include<iomanip>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include<malloc/malloc.h>
using namespace std;
#define MaxVertexNum 100

typedef int EdgeType;
typedef char VertexType[10];

typedef struct
{
    VertexType vertex[MaxVertexNum];            //顶点表
    EdgeType edges[MaxVertexNum][MaxVertexNum]; //邻接矩阵,即边表
    int vexnum,edgenum;        //顶点数和边数
}MGraph;

struct side
{
    int a,b;//边的两顶点序号
    int weight; //边的权值
};

int LocateVex(MGraph G,VertexType u)
{
    int i;
    for(i=0;i<G.vexnum;++i)
        if(strcmp(u,G.vertex[i])==0)
            return i;
    return -1;
}

void CreateMGraphAN(MGraph &G)
{
    int i,j,k,w;
    VertexType vi,vj;
    cout<<"请输入顶点数和边数(输入格式为:顶点数,边数):"<<endl;
    cin>>G.vexnum>>G.edgenum;
    cout<<"请输入顶点信息(输入格式为:顶点号<CR>):"<<endl;
    for(i=0;i<G.vexnum;i++)
        cin>>G.vertex[i];
    for(i=0;i<G.vexnum;i++)
        for(j=0;j<G.vexnum;j++)
            G.edges[i][j]=0;
   cout<<"请输入"<<G.edgenum<<"条边对应的两个顶点的序号及权值(输入格式为:顶点1,顶点2,权值)";
    for(k=0;k<G.edgenum;k++)
    {
        cout<<"顶点1,顶点2,权值:"<<endl;
        cin>>vi>>vj>>w;
        i=LocateVex(G,vi);
        j=LocateVex(G,vj);
        G.edges[i][j]=w;
        G.edges[j][i]=w;
    }   
}//CreateMGraphAN

void Kruskal(MGraph G)
{
    int set[MaxVertexNum],senumber=0,sb,i,j,k;
    side se[MaxVertexNum*(MaxVertexNum-1)/2];//存储边信息的一维数组
    for(i=0;i<G.vexnum;i++)
        for(j=i+1;j<G.vexnum;j++)
         if(G.edges[i][j]>0 && G.edges[i][j]<INFINITY)
         {
             k=senumber-1;
             while (k>=0)
                if(se[k].weight>G.edges[i][j])
                {
                    se[k+1]=se[k];
                    k--;
                }         
            else
                break;
            se[k+1].a=i;
            se[k+1].b=j;
            se[k+1].weight=G.edges[i][j];
            senumber++;
         }
    cout<<"i  se[i].a  se[i].b  se[i].weight"<<endl;
    for(i=0;i<senumber;i++)
         cout<<i<<setw(6)<<se[i].a<<setw(9)<<se[i].b<<setw(10)<<se[i].weight<<endl;
    for(i=0;i<G.vexnum;i++)
        set[i]=i;
    cout<<"最小代价生成树的各条边是:"<<endl;
    j=0;
    k=0;
    while(k<G.vexnum-1)
    {
        if(set[se[j].a]!=set[se[j].b])
        {
            cout<<G.vertex[se[j].a]<<"-"<<G.vertex[se[j].b]<<endl;
            sb=set[se[j].b];
            for(i=0;i<G.vexnum;i++)
            if(set[i]==sb)
                set[i]=set[se[j].a];
            k++;
        }
        j++;
    }
}
void print(MGraph G)
{
    int i,j;
    for(i=0;i<G.vexnum;i++)
    {   
        for(j=0;j<G.vexnum;j++)
            cout<<setw(4)<<G.edges[i][j];   
        cout<<endl;
    }
}
int main()
{
    MGraph G;
    CreateMGraphAN(G);
    cout<<"输出图的邻接矩阵:"<<endl;
    print(G);
    Kruskal(G);
}


---------------------------------------------------------------------------------------------------------------------
图片名称 图片名称
图4、图5
首先,主程序构造了图4所示的无向网。然后,调用 kruskal(),求该网的最小生成树。其中用到了辅助数组 set[]。set[i]表示第 i 个顶点所在的集合。设初态 set[i]=i,6个顶点分属于6个集合,如图5(a)所示。在邻接矩阵的上三角中找权值最小的边(因为是无向网),边(V1—V3)的权值最小,将V1和V3并到1个集合中。方法是将V3的集合set[2]赋值为set[0](V1的集合),同时将该边删除(令其上在三角的值为无穷)并输出该 边,如图5(b)所示。用此方法依次将V4和V6、V2和V5 分别并到1个集合中,如图5(c)、图5(d)所示。这时,邻接矩阵上三角中权值最小的边是(V3—V6),这 两顶点分属于两个集合0和3。将集合3合并到集合0中。方法是把集合3中的V4、V6都并到集合0中,如图5(e)所示。这时在邻接矩阵的上三角中首先找到的权值最小边是(V1—V4),但它们属于同一个集合(set[0]=set[3]=0),删除该边,继续查找。找到(V2—V3)是权值最小的边,且它们分属于不同的集合。把V3所在集合中的顶点都并到V2所在集合中,使所有顶点都在集合1中,如图5(f)所示,最后构成了最小生成树。程序运行结果和普里姆算法的一样。
---------------------------------------------------------------------------------------------------------------------
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值