数据结构最小生成树两个算法

在无向图中,若任意两个顶点vi与vj都有路径相通,则称该无向图为连通图。(强连通图:在有向图中,若任意两个顶点vi与vj都有路径相通,则称该有向图为强连通图。)
在连通图中,若图的边具有一定的意义,每一条边都对应着一个数,称为权;权代表着连接连个顶点的代价,称这种连通图叫做连通网。 一个连通图的生成树是指一个连通子图,它含有图中全部n个顶点,但只有足以构成一棵树的n-1条边。一颗有n个顶点的生成树有且仅有n-1条边,如果生成树中再添加一条边,则必定成环。在连通网的所有生成树中,所有边的代价和最小的生成树,称为最小生成树。最小生成树可以用kruskal(克鲁斯卡尔)算法或prim(普里姆)算法求出。
prim算法:
此算法可以称为“加点法”,每次迭代选择代价最小的边对应的点,加入到最小生成树中。算法从某一个顶点s开始,逐渐长大覆盖整个连通网的所有顶点。
1.图的所有顶点集合为V;初始令集合u={s},v=V−u;
2.在两个集合u,v能够组成的边中,选择一条代价最小的边(u0,v0),加入到最小生成树中,并把v0并入到集合u中。
3.重复上述步骤,直到最小生成树有n-1条边或者n个顶点为止。
Krustral算法:
此算法可以称为“加边法”,初始最小生成树边数为0,每迭代一次就选择一条满足条件的最小代价边,加入到最小生成树的边集合里。 
1. 把图中的所有边按代价从小到大排序; 
2. 把图中的n个顶点看成独立的n棵树组成的森林; 
3. 按权值从小到大选择边,所选的边连接的两个顶点ui,vi,应属于两颗不同的树,则成为最小生成树的一条边,并将这两颗树合并作为一颗树。 
4. 重复(3),直到所有顶点都在一颗树内或者有n-1条边为止。
//注释的代码本来是想用指针写prim算法,可是比较尴尬没写出来。
#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define MaxInt 1000000
#define MVNum 100
#define Vexcount 4
using namespace std;
typedef unsigned int  ArcType;//边的权值类型
typedef int VerTexType;       //顶点的权值类型
typedef struct
{
    VerTexType vexs[MVNum];   //顶点表
    ArcType arcs[MVNum][MVNum];//邻接矩阵
    int vexnum,arcnum;         //图的当前点数和边数
} AMGraph;
//typedef struct ArcNode         //边结点
//{
//    int adjvex;                //该边所指向的顶点的位置
//    int Valuel;                //该边的权值
//    struct ArcNode*nextarc;    //指向下一条边的指针
//} ArcNode;
//typedef struct VNode
//{
//    VerTexType data;           //顶点信息
//    ArcNode *firstarc;         //指向第一条依附该顶点的边的指针
//} VNOde,AdjList[MVNum];        //AdjList表示邻接表类型
//typedef struct
//{
//    AdjList vertices;          //邻接表
//    int vexnum,arcnum;         //图的当前顶点数和边数
//} ALGraph;
struct node
{
    ArcType adjvex;             //最小边在U中的那个顶点
    ArcType lowcost;            //最小边上的权值
} closedge[MVNum];              //辅助数组,用来记录从顶点集U到V-U的权值最小的边
struct nod
{
    VerTexType Head;              //边的始点
    VerTexType Tail;              //边的终点
    ArcType lowcost;           //边上的权值
} Edge[MVNum];
int Vexset[MVNum];
//int vis[MVNum];
bool cmp(nod Edge1,nod Edge2)
{
    return Edge1.lowcost<Edge2.lowcost;
}
int LocateVex(AMGraph G,int u)
{
    return u;
}
//int LocateVex(ALGraph G,int u)
//{
//    return u;
//}
void CreatUDN(AMGraph &G)
{
    //采用邻接矩阵表示法,创建无向网G
    int i,j,k,v1,v2,w;
    cin>>G.vexnum>>G.arcnum;     //输入总顶点数,总边数。
    for( i=1; i<=Vexcount; ++i)
        cin>>G.vexs[i];          //依次输入点的信息
    for( j=0; j<=G.vexnum; ++j)   //初始化邻接矩阵,边的权值均置为MaxInt
        for( i=1; i<=G.vexnum; ++i)
            G.arcs[i][j]=MaxInt;
    for( k=0; k<G.arcnum; ++k)   //构造邻接矩阵
    {
        cin>>v1>>v2>>w;          //输入一条边依附的顶点及权值
        i=LocateVex(G,v1);
        j=LocateVex(G,v2);
        Edge[k].Head=i;
        Edge[k].Tail=j;
        Edge[k].lowcost=w;
        G.arcs[i][j]=w;          //边<v1,v2>的权值置为w
        G.arcs[j][i]=G.arcs[i][j];//置<v1,v2>的对称边<v2,v1>的权值为w
    }
}//时间复杂度O(n^2)
//void CreatUDG(ALGraph &G)
//{
//    采用邻接表表示法,创建无向网G
//    int i,k,v1,v2,w,j;
//    ArcNode *p1,*p2;
//    cin>>G.vexnum>>G.arcnum;      //输入总顶点数,总边数
//    for( i=1; i<=G.vexnum; ++i)    //输入各点构造表头结点表
//    {
//        cin>>G.vertices[i].data;  //输入顶点值
//        G.vertices[i].firstarc=NULL;//初始化表头结点的指针域为NULL
//    }
//    for( k=1; k<=G.arcnum; ++k)     //输入各边,构造邻接表
//    {
//        cin>>v1>>v2>>w;            //输入一条边依附的两个顶点
//        i=LocateVex(G,v1);
//        j=LocateVex(G,v2);
//        p1=new ArcNode;            //生成一个新的边结点*p1
//        p1->adjvex=j;              //邻接点序号为j
//        p1->Valuel=w;              //边的权值
//        p1->nextarc=G.vertices[i].firstarc;
//        G.vertices[i].firstarc=p1;
//        将新结点*p1插入顶点vi的头部
//        p2=new ArcNode;             //生成另一个对称的新的边结点*p2
//        p2->adjvex=i;               //邻接点序号为i
//        p2->Valuel=w;               //边的权值
//        p2->nextarc=G.vertices[j].firstarc;
//        G.vertices[j].firstarc=p2;
//        将新结点*p2插入顶点vj的头部
//    }
//}//时间复杂度O(n+e)
int Min(struct node *closedge)
{
    unsigned int Min = MaxInt;
    int index = -1;
    for (int i = 1; i <=Vexcount; i++)
    {
        if (closedge[i].lowcost < Min && closedge[i].lowcost !=0)
        {
            Min = closedge[i].lowcost;
            index = i;
        }
    }
    return index;

}
void MiniSpanTree_Prim(AMGraph G,VerTexType u)
{
    int i,j,k,u0,v0,ans=0;
    //无向网G以邻接矩阵形式存储,从顶点u出发构造G的最小生成树T,输出T的各条边
    k=LocateVex(G,u);
    for( j=1; j<=G.vexnum; ++j)
    {
        if(j!=k)
        {
            closedge[j].adjvex=k;
            closedge[j].lowcost=G.arcs[k][j];
        }
    }
    closedge[k].lowcost=0;                     //初始
    closedge[k].adjvex=k;
    for( i=2; i<=G.vexnum; ++i)                  //选择其余n-1个顶点,生成n-1条边
    {
        k=Min(closedge);
        //求出T的下一个结点:第k个顶点,closedge[k]中存有当前最小边
        u0=closedge[k].adjvex;
        v0=G.vexs[k];
        ans+=closedge[k].lowcost;
        cout<<u0<<"=="<<v0<<endl;
        closedge[k].lowcost=0;
        for( j=1; j<=G.vexnum; ++j)
            if(G.arcs[k][j]<closedge[j].lowcost)
            {
                closedge[j].adjvex=k;
                closedge[j].lowcost=G.arcs[k][j];
            }
    }
    cout<<"MiniSpanTree_Prim answer="<<ans<<endl;
}
//void MiniSpanTree_ListPrim(ALGraph G,VerTexType u)
//{
//    unsigned int Min=MaxInt;
//    int i,j,k,ans=0,index=-1;
//    memset(vis,0,sizeof(vis));
//    k=u;
//    for(i=1; i<G.vexnum; ++i)
//    {
//        Min=MaxInt;
//        index=-1;
//        ArcNode *p;
//        for( p=G.vertices[k].firstarc; p!=NULL; p=p->nextarc)
//        {
//            j=p->adjvex;
//            if(p->Valuel <Min&&vis[j]==0)
//            {
//                vis[j]=1;
//                Min=p->Valuel;
//                index=j;
//            }
//        }
//        for(j=1;j<G.vexnum;j++){
//            if(!vis[j]&&>map[k][j])
//            dis[j]=map[k][j];//更新最短距离
//        cout<<k<<"-**-"<<index<<endl;
//        ans+=Min;
//        k=index;
//    }
//    cout<<"MiniSpanTree_ListPrim answer="<<ans<<endl;
//}
void MiniSpanTree_Kruskal(AMGraph G)
{
    int i,j,v1,v2,vs1,vs2,ans=0;
    //无向网G以邻接矩阵形式存储,构造G的最小生成树T,输出T的各条边
    sort(Edge,Edge+G.arcnum,cmp);                       //将数组Edge中的元素按权值从小到大排序
    for( i=0; i<=G.vexnum; ++i)                      //辅助数组,表示各顶点自成一个连通分量
        Vexset[i]=i;
    for( i=0; i<G.arcnum; ++i)
    {
        //依次查看排好序的数组Edge中的边是否在同一连通分量上
        v1=LocateVex(G,Edge[i].Head);                //v1为边的始点
        v2=LocateVex(G,Edge[i].Tail);                //v2位边的终点
        vs1=Vexset[v1];                              //获取v1所在连通分量
        vs2=Vexset[v2];                              //获取v2所在连通分量
        if(vs1!=vs2)                                 //两个顶点分属不同的连通分量
        {
            ans+=Edge[i].lowcost;
            cout<<Edge[i].Head<<"--"<<Edge[i].Tail<<endl;  //输出此边
            for(j=1; j<=G.vexnum; ++j)                      //合并vs1和vs2两个分量。即两个集合统一编号
                if(Vexset[j]==vs2) Vexset[j]=vs1;          //集合编号为vs2的都改为vs1
        }
    }
    cout<<"MiniSpanTree_Kruskal answer="<<ans<<endl;
}
int main()
{
    AMGraph G;
//    ALGraph G1;
    CreatUDN(G);
//    CreatUDG(G1);
    MiniSpanTree_Prim(G,1);
//    MiniSpanTree_ListPrim(G1,1);
    MiniSpanTree_Kruskal(G);
}
posted @ 2017-12-29 19:13  _大美  阅读(802)  评论(0编辑  收藏  举报