zoukankan      html  css  js  c++  java
  • 最小生成树心得

    定义:

    在一个连通图G中,如果取它的全部顶点一部分边构成一个子图G’ , 即:

    V( G ' ) = V( );

    E( G ')E( );

    若边集E( G ')中的边既将图中的所有顶点联通不形成回路(回路:首尾节点相同,则称子图G'是原图G的一颗生成树。

    一颗含有n个点的生成树,必含有n-1条边。

    具有权最小的生成树称为最小生成树。

    归纳:

    生成树:

    • 无向连通图的边的集合
    • 无回路
    • 连接所有点最小
    • 所有边的权值之和最小

    算法:

    • prim算法
    • kruskal算法

             Prim算法实现

      .图节点数目为n,正在构造的生成树为T

      .维护dis数组,dis[i]表示vi到T的距离

      .开始所有的dis[i]=无穷大,T为空集

      1.若|T|=n,最小生成树完成。否则取dis[i]最小的不在T中的点vi加入T

      2.更新所有与有边相连且不在T中的点vj的dis值:dis[j]=min(dis[j] , w(vi,vj));

      3.转到1;

      

           关键问题

      .每次如何从连接T中和T外顶点的所有边中,找到一条最短的。

      1.如果用邻接矩阵存图,而且选取最短边的时候遍历所有点进行选取,则总时间复杂度为(V²),V为顶点个数。

      2.用邻接表存图,并使用堆来选取最短边,则总时间复杂度为O(ElogV)

      3.不加堆优化的Prim算法适用于密集图(稠密图),加堆优化的适用于稀疏图。

    #include <iostream>
    #include <vector>
    #include <algorithm>
    #include <queue>
    using namespace std;
    const int INFINITE = 1 << 30;
    struct Edge
    {
    int v; //边端点,另一端点已知
    int w; //边权值,也用来表示v到在建最小生成树的距离
    Edge(int v_ = 0, int w_ = INFINITE):v(v_),w(w_) { }
    bool operator <(const Edge & e) const
    {
    return w > e.w; //在队列里,边权值越小越优先
    }
    };
    vector< vector <Edge> > G(110); //图的邻接表int HeapPrim(const vector<vector<Edge> > & G, int n)
    //G是邻接表,n是顶点数目,返回值是最小生成树权值和
    {
    int i,j,k;
    Edge xDist(0,0);
    priority_queue<Edge> pq; //存放顶点及其到在建生成树的距离
    vector<int> vDist(n); //各顶点到已经建好的那部分树的距离
    vector<int> vUsed(n);//标记顶点是否已经被加入最小生成树
    int nDoneNum = 0; //已经被加入最小生成树的顶点数目
    for( i = 0;i < n;i ++ ) {
    vUsed[i] = 0;
    vDist[i] = INFINITE;
    }
    nDoneNum = 0;
    int nTotalW = 0; //最小生成树总权值
    pq.push(Edge(0,0)); //开始只有顶点0,它到最小生成树距离0while( nDoneNum < n && !pq.empty() ) {
    do {//每次从队列里面拿离在建生成树最近的点
    xDist = pq.top(); pq.pop();
    } while( vUsed[xDist.v] == 1 && ! pq.empty());
    if( vUsed[xDist.v] == 0 ) {
    nTotalW += xDist.w; vUsed[xDist.v] = 1; nDoneNum ++;
    for( i = 0;i < G[xDist.v].size();i ++ ) {//更新新加入点的邻点
    int k = G[xDist.v][i].v;
    if( vUsed[k] == 0) {
    int w = G[xDist.v][i].w ;
    if( vDist[k] > w ) {
    vDist[k] = w;
    pq.push(Edge(k,w));
    }
    }
    }
    }
    }
    if( nDoneNum < n )
    return -1; //图不连通
    return nTotalW;
    }
    考察了所有的边,且考察一条边时 可能执
    行 pq.push(Edge(k,w)) 故复杂度O(ELogV)int main()
    {
    int N;
    while(cin >> N) {
    for( int i = 0;i < N; ++i)
    G[i].clear();
    for( int i = 0; i < N; ++i)
    for( int j = 0; j < N; ++j) {
    int w;
    cin >> w;
    G[i].push_back(Edge(j,w));
    }
    cout << HeapPrim(G,N) << endl;
    }
    }
    POJ1258 prim+堆 优先队列

                      Kruskal算法

      .假设G=(V,E)是一个具有n个顶点的连通图,T=(U,TE)是G的最小生成树,U=V,TE初值为空。

      .将图G中的边按权值从小到大依次选取,若选取的边使生成树不形成回路,则把它并入TE中,若形成回路则将其舍弃,知道TE中包含n-1条边为止,此时T为最小生成树。

  • 相关阅读:
    视图的作用,视图可以更改么?
    数据库事务的四个特性及含义
    mysql 设置隔离级别
    如何避免事务的并发问题?
    事务控制语言(TCL)
    事务的并发问题有哪些?
    事务的隔离级别: 事务并发问题如何发生?
    DDL 语言
    DML 语言
    TRUNCATE、Drop、Delete 的用法
  • 原文地址:https://www.cnblogs.com/Roni-i/p/7454735.html
Copyright © 2011-2022 走看看