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


    在无向图中,若任意两个顶点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);
    }
  • 相关阅读:
    Nginx 使用 X-Accel-Redirect 实现静态文件下载的统计、鉴权、防盗链、限速等
    SpringCloud的Feign相关调用,熔断源码分析
    SpringCloud之熔断(六)
    lambda与java8函数式编程
    CompletableFuture基本使用
    RabbitMq消费者在初始配置之后进行数据消费
    文件监控性能问题【BUG】
    element穿梭框el-transfer增加拖拽排序和shift多选checkbox功能
    RPC序列化方式优缺点
    部署企业私密信息管理平台Hashicorp vault集成kubernetes和AWS的密钥信息
  • 原文地址:https://www.cnblogs.com/da-mei/p/9053284.html
Copyright © 2020-2023  润新知