/*****/关于图2.0

本文介绍了最小生成树的概念及其两种主要构建算法:Kruskal算法和Prim算法。详细讲解了这两种算法的具体实现过程,并通过邻接矩阵和邻接表两种方式表示图结构。

最小生成树(无向图):
一个连通图,图中有N个顶点。我们能在连通图中选取N-1条边将图的所有顶点连在一起且不构成回路,则这N-1条边就构成了一个生成树。如果这N-1条边的权值和最小,则就构成了最小生成树。
构造最小生成树时有两个算法,Kruskal算法和Prim算法,都使用了贪心法求解。
注意:使用贪心算法在求解最小生成树的时候总是得到了局部最优解,但是整体结果不一定最优解。

Kruskal算法:Kruskal算法是一种找边的思想。假设初始时有两个存放边的集合为E1和E2,E1里面存放了连通图的所有边,E2是空集合。
1、在E1里面找一条权值最小的边edge,并将edge从E1里面删除
2、判断edge是否与E2里面的边能形成回路。
3、如果构成回路则就丢弃这条边,从步骤1重新开始。
4、如果不构成回路,则就将edge加入到E2中。
5、如果E2中有N-1条边的话,则就停止,这时候最小生成树就已经找出来了。否则就重复上述步骤。

利用最小堆来找最短的边,将连通图的所有边构成一个最小堆。

用一个并查集ufs,将连通图的所有顶点都加入到并查集里面,将E2里面的所有边的顶点都并入一个集合,这样的话如果edge的两个顶点是在一个集合中,则它就会构成回路,否则不会构成回路。

实现:将最小生成树存放在minTree里面

bool MinTree(GraphList<V,E>& minTree)  
       {  
              if (_isdirect)             //如果是有向图的话,则就直接返回false  
              {  
                     return false;  
              }  
              minTree._vertex = _vertex;        //将这个图的所有顶点都放入到minTree里面  
              minTree._tables.resize(_tables.size());  
              struct Compare  
              {  
                     bool operator()(Node* l,Node* r)  
                     {  
                           return (l->_weight) >(r->_weight);  
                     }  
              };  
              //建立一个小堆,用来查找图里面最小的边  
              vector<Node*> minHeap;  
              for (size_t i = 0; i < _tables.size();++i)  
              {  
                     Node* cur = _tables[i];  
                     while (cur)  
                     {  
                           minHeap.push_back(cur);  
                           cur = cur->_next;  
                     }  
              }  
              make_heap(minHeap.begin(),minHeap.end(),Compare());    //建小堆  
              size_t n = 0;  
              UnionSet ufs(_vertex.size());  
              while (n < _tables.size() - 1)  
              {  
                     if (minHeap.empty())  
                     {  
                           return false;  
                     }  
                     pop_heap(minHeap.begin(),minHeap.end(),Compare());  
                     Node* minEdge = minHeap.back();     //得到最小边  
                     minHeap.pop_back();  
                     //到并查集里面判断加入这条边会不会形成环  
                     size_t root1 = ufs.GetRoot(minEdge->_src);  
                     size_t root2 = ufs.GetRoot(minEdge->_dst);  
                     if (root1!=root2)                                    //这两个结点不再并查集里面  
                     {  
                           ufs.Merge(minEdge->_src, minEdge->_dst);         //把这条边添加到并查集里面  
                           minTree._AddEdge(minEdge->_src, minEdge->_dst,minEdge->_weight);  //添加这条边  
                           n++;  
                     }  
              }  
              return true;  
       }  

Prim算法:一种找点的思想。初始时有两个存放点的集合V1和V2,V1里面存放了连通图里面任意的N-1个顶点,V2是里面存放了1个顶点。
1、在V1和V2里面各选一个点,将他们组成的边的所有情况存放在集合E里面。
2、假设v1和v2是分别从V1和V2里面选择出来的点,并且v1和v2构成的边是E里面最小的。
3、将v1加入到V2里面,并将v1从V1里面删除。
4、如果V1不为空,则重复上述步骤。

利用最小堆来找E里面最短的边,将E中的所有边构成一个最小堆。

可以用一个并查集ufs,将连通图的所有顶点都加入到并查集里面,将E2里面的所有边的顶点都并入一个集合,这样的话如果edge的两个顶点是在一个集合中,则它就会构成回路,否则不会构成回路.

实现:将最小生成树存放在minTree里面

//最小生成树就是用N-1条边将图中的所有顶点都连接起来,而且这些边的权重之和要最小  
       //普里姆算法  
       bool MinTree(GraphList<V, E>& minTree)  
       {  
              if (_isdirect)             //如果是有向图的话,则就直接返回false  
              {  
                     return false;  
              }  
              minTree._vertex = _vertex;        //将这个图的所有顶点都放入到minTree里面  
              minTree._tables.resize(_tables.size());  
              struct Compare  
              {  
                     bool operator()(Node* l,Node* r)  
                     {  
                           return l->_weight > r->_weight;  
                     }  
              };  
              vector<Node*> minHeap;  
              UnionSet ufs(_vertex.size());         //建立一个并查集,最开始每个顶点都是一个单独的集合  
              size_t n = 0;  
              Node* cur = _tables[0];  
              while (n<_tables.size()-1)  
              {  
                     while (cur)                        //将这个点所有的边都放入最小堆中  
                     {  
                           minHeap.push_back(cur);  
                           push_heap(minHeap.begin(),minHeap.end(),Compare());  
                           cur = cur->_next;  
                     }  
                     pop_heap(minHeap.begin(),minHeap.end(),Compare());  
                     if (minHeap.empty())  
                     {  
                           return false;  
                     }  
                     Node* minEdge = minHeap.back();  
                     minHeap.pop_back();  
                     size_t root1 =ufs.GetRoot(minEdge->_src);  
                     size_t root2 =ufs.GetRoot(minEdge->_dst);  
                     if (root1!=root2)  
                     {  
                           ufs.Merge(minEdge->_src, minEdge->_dst);  
                           minTree._AddEdge(minEdge->_src, minEdge->_dst, minEdge->_weight);  
                           cur = _tables[minEdge->_dst];  
                           n++;  
                     }  
              }  
              return true;  
       }  

邻接矩阵表示法

//邻接矩阵表示图  
template<typename V,typename E>  
class GraphMatrix  
{  
public:  
    GraphMatrix(V* vertax,size_t n,bool isdirect=false)   //默认是无向图  
        :_size(n)  
        , _isdirect(isdirect)  
    {  
        _vertex = new V[n];             //开辟n个大小的数组用来表示图的顶点  
        _matrix = new E* [n];          //开辟一个n*n的二维数组用来保存边  
        for (size_t i = 0; i < n; ++i)  
        {  
            _vertex[i] = vertax[i];  
            _matrix[i] = new E[n];  
            for (size_t j = 0; j < n; ++j)  
            {  
                _matrix[i][j] =E();  
            }  
        }  
    }  

    ~GraphMatrix()  
    {  
        delete[] _vertex;  
        for (size_t i = 0; i < _size; ++i)  
        {  
            delete[] _matrix[i];  
        }  
        delete [] _matrix;  
    }  

    void AddEdge(const V& src,const V& dst,const E& w)  
    {  
        size_t line = GetIndex(src);  
        size_t row = GetIndex(dst);  
        _matrix[line][row] = w;  
        if (_isdirect == false)    //无向图  
        {  
            _matrix[row][line] = w;  
        }  
    }  
protected:  
    size_t GetIndex(const V& src)    //得到顶点所在_vertex中的下标  
    {  
        for (size_t i = 0; i < _size; ++i)  
        {  
            if (_vertex[i] == src)  
                return i;  
        }  

        assert(false);  
        return -1;  
    }  

private:  
    V* _vertex;     //用来保存顶点  
    E** _matrix;    //用来保存边  
    size_t _size;  
    bool _isdirect;     //判断是不是无向图  
};  

邻接表表示法

template<typename V,typename E>  
class GraphList  
{  
public:  
    struct Node         //内部类,在邻接表上挂的结点的类型  
    {  
        size_t _src;    //_tables[i]在_vertex中的位置  
        size_t _dst;    //与_tables[i]有边的点在_vertex中的位置  
        E _weight;  
        Node* _next;  
        Node(size_t src,size_t dst,E w)  
            :_src(src)  
            , _dst(dst)  
            , _weight(w)  
            , _next(NULL)  
        {}  
    };  
public:  
    GraphList(bool isdirect = false)  
        :_isdirect(isdirect)  
    {}  
    GraphList(V* vertex,size_t n,bool isdirect=false)  
        :_vertex(vertex,vertex+n)  
        , _isdirect(isdirect)  
    {  
        _tables.resize(n);  
    }  


    ~GraphList()  
    {  
        for (size_t i = 0; i < _tables.size(); ++i)  
        {  
            Node* cur = _tables[i];  
            while(cur)  
            {  
                Node* del = cur;  
                cur = cur->_next;  
                delete del;  
            }  
        }  
    }  


    void AddEdge(const V& src,const V& dst,const E& w)     //添加边  
    {  
        size_t index1 = GetIndex(src);  
        size_t index2 = GetIndex(dst);  
        if (_isdirect == false)         //无向图  
        {  
            _AddEdge(index1,index2,w);  
            _AddEdge(index2,index1,w);  
        }  
        else           //有向图  
        {  
            _AddEdge(index1,index2,w);  
        }  
    }  


    void DFS(const V& src)            //深度优先遍历  
    {  
        vector<bool> visited(_vertex.size(),false);  
        _DFS(GetIndex(src),visited);  
        for (size_t i = 0; i < visited.size(); ++i)      //如果图不是连通图的话  
        {  
            if (visited[i]==false)  
                _DFS(i, visited);  
        }  
    }  


    void  BFS(const V& src)  
    {  
        size_t index = GetIndex(src);  
        vector<bool> visited(_vertex.size(),false);  
        queue<size_t> q;         
        q.push(index);              //将起点的坐标入队  


        while (!q.empty())  
        {  
            size_t front = q.front();  
            q.pop();  


            cout << _vertex[front].c_str() << "->";  
            visited[front] = true;  


            Node* cur = _tables[front];  
            while (cur)  
            {  
                if (visited[cur->_dst] == false)  
                {  
                    q.push(cur->_dst);  
                    visited[cur->_dst] = true;  //如果这个元素已经入队了,则就标记为true  
                }  
                cur = cur->_next;  
            }  
        }  
    }  


    ////最小生成树就是在用N-1条边将一个连通图里面的所有顶点都连接起来,并且所有边的权重之和最小  
    ////克鲁斯卡尔算法,寻找最短的边,如果不构成回路的话,则就是最小生成树中的一条边  
    //bool MinTree(GraphList<V,E>& minTree)  
    //{  
    //  if (_isdirect)             //如果是有向图的话,则就直接返回false  
    //  {  
    //      return false;  
    //  }  


    //  minTree._vertex = _vertex;        //将这个图的所有顶点都放入到minTree里面  
    //  minTree._tables.resize(_tables.size());  


    //  struct Compare  
    //  {  
    //      bool operator()(Node* l,Node* r)  
    //      {  
    //          return (l->_weight) >(r->_weight);  
    //      }  
    //  };  


    //  //建立一个小堆,用来查找图里面最小的边  
    //  vector<Node*> minHeap;  
    //  for (size_t i = 0; i < _tables.size();++i)  
    //  {  
    //      Node* cur = _tables[i];  
    //      while (cur)  
    //      {  
    //          minHeap.push_back(cur);  
    //          cur = cur->_next;  
    //      }  
    //  }  
    //  make_heap(minHeap.begin(),minHeap.end(),Compare());    //建小堆  


    //  size_t n = 0;  
    //  UnionSet ufs(_vertex.size());  
    //  while (n < _tables.size() - 1)  
    //  {  
    //      if (minHeap.empty())  
    //      {  
    //          return false;  
    //      }  
    //      pop_heap(minHeap.begin(),minHeap.end(),Compare());  
    //      Node* minEdge = minHeap.back();     //得到最小边  
    //      minHeap.pop_back();  


    //      //到并查集里面判断加入这条边会不会形成环  
    //      size_t root1 = ufs.GetRoot(minEdge->_src);  
    //      size_t root2 = ufs.GetRoot(minEdge->_dst);  
    //      if (root1!=root2)                                    //这两个结点不再并查集里面  
    //      {  
    //          ufs.Merge(minEdge->_src, minEdge->_dst);         //把这条边添加到并查集里面  
    //          minTree._AddEdge(minEdge->_src, minEdge->_dst,minEdge->_weight);  //添加这条边  
    //          n++;  
    //      }  
    //  }  
    //  return true;  
    //}  






//最小生成树就是用N-1条边将图中的所有顶点都连接起来,而且这些边的权重之和要最小  
//普里姆算法  
    bool MinTree(GraphList<V, E>& minTree)  
    {  
        if (_isdirect)             //如果是有向图的话,则就直接返回false  
        {  
            return false;  
        }  
        minTree._vertex = _vertex;        //将这个图的所有顶点都放入到minTree里面  
        minTree._tables.resize(_tables.size());  


        struct Compare  
        {  
            bool operator()(Node* l,Node* r)  
            {  
                return l->_weight > r->_weight;  
            }  
        };  


        vector<Node*> minHeap;  
        UnionSet ufs(_vertex.size());         //建立一个并查集,最开始每个顶点都是一个单独的集合  
        size_t n = 0;  


        Node* cur = _tables[0];  
        while (n<_tables.size()-1)  
        {  
            while (cur)                        //将这个点所有的边都放入最小堆中  
            {  
                minHeap.push_back(cur);  
                push_heap(minHeap.begin(),minHeap.end(),Compare());  
                cur = cur->_next;  
            }  
            pop_heap(minHeap.begin(),minHeap.end(),Compare());  
            if (minHeap.empty())  
            {  
                return false;  
            }  
            Node* minEdge = minHeap.back();  
            minHeap.pop_back();  


            size_t root1 =ufs.GetRoot(minEdge->_src);  
            size_t root2 =ufs.GetRoot(minEdge->_dst);  
            if (root1!=root2)  
            {  
                ufs.Merge(minEdge->_src, minEdge->_dst);  
                minTree._AddEdge(minEdge->_src, minEdge->_dst, minEdge->_weight);  
                cur = _tables[minEdge->_dst];  
                n++;  
            }  
        }  
        return true;  
    }  

protected:  
    void _DFS(size_t src,vector<bool>& visited)  
    {  
        Node* cur = _tables[src];  
        printf("%s:->", _vertex[src].c_str());  
        visited[cur->_src] = true;                  //将已经访问过的结点标记起来  


        while (cur)  
        {  
            if (visited[cur->_dst] == false)  
                _DFS(cur->_dst, visited);  
            cur = cur->_next;  
        }  
    }  


    void _AddEdge(size_t src, size_t dst, const E& w)  
    {  
        Node* node = new Node(src,dst,w);  


        node->_next = _tables[src];  
        _tables[src] = node;  
    }  


    size_t GetIndex(const V& src)  
    {  
        for (size_t i = 0; i < _vertex.size(); ++i)  
        {  
            if (_vertex[i] == src)  
                return i;  
        }  
        assert(false);  
        return -1;  
    }  
private:  
    vector<V> _vertex;       //用来存储顶点  
    vector<Node*> _tables;  
    bool _isdirect;  
};  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值