【图】图的遍历以及最小生成树

来源:互联网 发布:js控制div旋转 编辑:程序博客网 时间:2024/06/05 10:12

图的遍历

图的遍历图和树的遍历类似,那就是从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次,这个过程就叫做图的遍历。

对于图的遍历来说,如何避免因回路陷入死循环,就需要科学地设计遍历方案,通过有两种遍历次序方案:深度优先遍历和广度优先遍历。

1、深度优先遍历 DFS

深度优先遍历(Depth_First_Search),也称为深度优先搜索,简称DFS。

沿着树的深度遍历树的节点,尽可能深的搜索树的分支节点,当节点v的所有边被访问过,搜索将回溯到发现节点v的那条边的起始位置,然后继续访问未被访问过的节点,直到所有的节点被访问。
此就需要标记访问过的节点。

这里写图片描述

类似于二叉树的前序遍历。

代码时基于邻接表实现的。
对于图来说,不一定都是连通图,所以深度优先遍历完其中的连通图后,还要查看还有节点是非连通图未访问的。

//深度优先遍历:    void DFS(const V& v)    {        cout << "深度优先遍历:" << endl;        //标记访问过的节点        vector<bool> visited(_vex.size(), false);        int idx = GetIndexOfVertex(v);        _DFS(idx, visited);        //非连通的访问        for (size_t i = 0; i < _vex.size(); ++i)        {            if (!visited[i])                _DFS(i, visited);        }        cout << "NULL" << endl;    }    void _DFS(size_t idx,vector<bool>& visitted)    {        //未被访问的节点,做广度搜索        if(!visited[idx])        {            cout << idx << "-->";            visited[idx] = true;            //邻接表,所以访问单链表            LinkEdge<W>* pCur = _LinkTable[idx];            while (pCur)            {                _DFS(pCur->_desIndex, visited);                pCur = pCur->_pNext;            }        }    }

2、广度优先遍历 BFS
BFS是从根节点开始出发,沿着树的宽度遍历树的节点,直到所有的节点被访问。
有点类似于树的层次遍历。

这里写图片描述

下面代码时基于邻接表实现的

//广度优先遍历图:队列    void BFS(const V& v)    {        cout << "广度优先遍历:" << endl;        vector<bool> visited(_vex.size(), false);        _BFS(v, visited);        //非连通的访问        for (size_t i = 0; i < _vex.size(); ++i)        {            if (!visited[i])                _BFS(_vex[i],visited);        }        cout << "NULL" << endl;    }    void _BFS(const V& v,vector<bool>& visited)    {        queue<size_t> q;        int idx = GetIndexOfVertex(v);        q.push(idx);        while(!q.empty())        {            if (!visited[idx])            {                cout << idx << "-->";                visited[idx] = true;                q.pop();                LinkEdge<W>* pCur = _LinkTable[idx];                while (pCur)                {                    q.push(pCur->_desIndex);                    pCur = pCur->_pNext;                }            }        }           }

图的最小生成树

生成树是将图中所有顶点以最少的边连通的子图。
权值和最小的生成树就是最小生成树。

从最小生成树的定义可知,构造有n个结点的无向连通带权图的最小生成树,必须满足以下三条:
(1)构造的最小生成树必须包括n个结点;
(2)构造的最小生成树中有且只有n-1条边;
(3)构造的最小生成树中不存在回路。

构造最小生成树的方法有许多种,典型的构造方法有两种,一种称作普里姆(Prim)算法,另一种称作克鲁斯卡尔(Kruskal)算法。

普里姆算法

普里姆算法思想是:利用贪心的算法。

从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。

(1)输入:一个加权连通图,其中顶点集合为V,边集合为E;

(2)初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {};

(3)重复下列操作,直到Vnew = V:
在集合E中选取权值最小的边(u, v),其中u为集合Vnew中的元素,而v则不是(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
将v加入集合Vnew中,将(u, v)加入集合Enew中;

(4)输出:使用集合Vnew和Enew来描述所得到的最小生成树。

这里写图片描述

基于邻接表代码实现
在寻找最小值,可以借助堆,

//比较器struct Biger    {        bool operator()(LinkEdge<W>* Left, LinkEdge<W>* Right)        {            return Left->_edge >  Right->_edge;        }    };//最小生成树prime算法    pair<GraphLink<V, W>, bool> Prime(const V& vertex)    {        //定义一个图来接收最小生成树,下面初始化图g        GraphLink<V, W> g;        g._vex.resize(_vex.size());        for (size_t idx = 0; idx < _vex.size(); ++idx)            g._vex[idx] = _vex[idx];        g._IsDirected = false;        g._LinkTable.resize(_vex.size()-1, NULL);        //给一个起始顶点,将顶点为起点的边放到最小堆中,每次从最小堆中取堆顶加到图中(判断是否已经加入)        vector<bool> flag(_vex.size(), false);//标记顶点是否已经加入图中         vector<LinkEdge<W> *> vEdge;        int index = GetIndexOfVertex(vertex);        LinkEdge<W> *pCur = _LinkTable[index];        while (pCur)        {            vEdge.push_back(pCur);            pCur = pCur->_pNext;        }        //构建堆        make_heap(vEdge.begin(), vEdge.end(), Biger());        int count = 0;//统计加入边的数量,n-1条边就是最小生成树        while (true)        {            //从最小堆中取出            pCur = vEdge[0];            pop_heap(vEdge.begin(), vEdge.end(), Biger());            vEdge.pop_back();            if (!flag[pCur->_desIndex])            {                g._AddEdge(pCur->_srcIndex, pCur->_desIndex, pCur->_edge);                if (++count == _vex.size() - 1)                    return make_pair(g, true);                flag[pCur->_srcIndex] = true;            }            index = pCur->_desIndex;            if (!flag[index])            {                pCur = _LinkTable[index];                while (pCur)                {                    if (!flag[pCur->_desIndex])                    {                        vEdge.push_back(pCur);                        push_heap(vEdge.begin(), vEdge.end(), Biger());                    }                       pCur = pCur->_pNext;                }               }        }        return make_pair(g, false);    }

2、克鲁斯卡尔算法

其思想就是直接了当的贪心,每次都将权值最短的边收进来:可以将每个顶点都看成一棵树,然后将权值最短的边的顶点连接起来,在不构成回路的情况下,将这些森林合并成一棵树。

考虑到构成回路的问题,这里我用到并查集。
并查集的实现 可以看下一篇博客,)

并查集的实现

    //排序比较器    struct Compare    {        bool operator()(LinkEdge<W>* Left, LinkEdge<W>* Right)        {            return Left->_edge <  Right->_edge;        }    };    //无向图的最小生成树    pair<GraphLink<V, W>, bool> GetMinTree()    {        //1 将所有的边放在vector中,过滤掉重复的边        vector<LinkEdge<W> *> edge;        for (size_t i = 0; i < _LinkTable.size(); ++i)        {            LinkEdge<W> * pCur = _LinkTable[i];            while (pCur)            {                if (pCur->_srcIndex < pCur->_desIndex)//过滤掉重复的边                    edge.push_back(pCur);                pCur = pCur->_pNext;            }        }        //2 将vector 排序        sort(edge.begin(), edge.end(), Compare());        //生成图,保存结果        GraphLink<V, W> g;        g._vex.resize(_vex.size());        for (size_t idx = 0; idx < _vex.size(); ++idx)            g._vex[idx] = _vex[idx];        g._LinkTable.resize(_LinkTable.size(), NULL);        g._IsDirected = false;        //3 从vector中取出最小值,添加到图中        //并查集初始化        UnionFindSet un(_vex.size());        int count = 0;//统计加入的边数量        for (size_t j = 0; j < edge.size(); ++j)        {            LinkEdge<W>* pEdge = edge[j];            //检测是否构成了环            if (!(un.IsSameSet(pEdge->_srcIndex,pEdge->_desIndex)))            {                g._AddEdge(pEdge->_srcIndex,pEdge->_desIndex, pEdge->_edge);                un.Union(pEdge->_srcIndex, pEdge->_desIndex);                if (++count == _vex.size() - 1)                {                    g._LinkTable.resize(count);                    return make_pair(g, true);                }                           }        }        return make_pair(g, false);    }
阅读全文
0 0
原创粉丝点击