zoukankan      html  css  js  c++  java
  • 图 续3

    ------------------siwuxie095

       

       

       

       

       

       

       

       

    程序 1:

       

    1)使用邻接矩阵存储无向图

       

    2)使用深度优先搜索(DFS)和广度优先搜索(BFS)进行遍历

       

       

    Node.h:

       

    #ifndef NODE_H

    #define NODE_H

       

    class Node

    {

    public:

    //默认有参构造函数

    Node(char data = 0);

       

    char m_cData;

       

    //标识当前节点有没有被访问过

    //被访问过就置为true

    bool m_bIsVisited;

    };

       

       

    #endif

       

       

       

    Node.cpp:

       

    #include "Node.h"

       

    Node::Node(char data)

    {

    m_cData = data;

    m_bIsVisited = false;

    }

       

       

       

    CMap.h:

       

    #ifndef CMAP_H

    #define CMAP_H

       

    #include "Node.h"

    #include <vector>

    using namespace std;

       

       

    //之所以在Map前加个C,是因为Map本身是标准模板库的类名,

    //为了能够区分,就在前面加了一个C,叫做CMap

    class CMap

    {

    public:

    CMap(int capacity);

    ~CMap();

    bool addNode(Node *pNode); //向图中加入顶点(节点)

    void resetNode(); //重置顶点:将每一个顶点设为未访问过

       

    //为有向图设置邻接矩阵,默认权值为1

    bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1);

    //为无向图设置邻接矩阵,默认权值为1

    bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1);

       

    void printMatrix(); //打印邻接矩阵

    void depthFirstTraverse(int nodeIndex); //深度优先遍历

    void breadthFirstTraverse(int nodeIndex); //广度优先遍历

       

    private:

    //从矩阵中获取权值(该函数不是公共使用的,而是内部使用的)

    bool getValueFromMatrix(int row, int col, int &val);

    //广度优先遍历实现函数

    void breadthFirstTraverseImpl(vector<int> preVec);

       

    private:

    int m_iCapacity; //图中最多可以容纳的顶点数

    int m_iNodeCount; //已经添加的顶点(节点)个数

    Node *m_pNodeArray; //用来存放顶点数组

    int *m_pMatrix; //用来存放邻接矩阵

    };

       

       

    #endif

       

       

       

    CMap.cpp:

       

    #include "CMap.h"

    #include <vector>

    #include <iostream>

    using namespace std;

       

       

    CMap::CMap(int capacity)

    {

    m_iCapacity = capacity;

    m_iNodeCount = 0;

    m_pNodeArray = new Node[m_iCapacity];

    //邻接矩阵--二维数组(实际上是用一维数组来表示二维数组)

    m_pMatrix = new int[m_iCapacity*m_iCapacity];

    //memset()函数:进行内存的设定

    //初始化邻接矩阵,将当前二维数组中的每一个元素都置为0

    memset(m_pMatrix,0,m_iCapacity*m_iCapacity*sizeof(int));

       

    ////也可以采用for循环来初始化邻接矩阵

    //for (int i = 0; i < m_iCapacity*m_iCapacity; i++)

    //{

    // m_pMatrix[i] = 0;

    //}

    }

       

       

    CMap::~CMap()

    {

    delete[]m_pNodeArray;

    m_pNodeArray = NULL;

    delete[]m_pMatrix;

    m_pMatrix = NULL;

    }

       

       

    bool CMap::addNode(Node *pNode)

    {

    //判断传入进来的节点是否为空

    if (NULL == pNode)

    {

    return false;

    }

    //顶点索引就是m_pNodeArray这个数组的下标

    m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;

    m_iNodeCount++;

    return true;

    }

       

       

    //将图中所有节点的访问标识置为false

    void CMap::resetNode()

    { //将图中已经存放的所有顶点设为未被访问过

    //不管之前是否被访问过

    for (int i = 0; i < m_iNodeCount; i++)

    {

    m_pNodeArray[i].m_bIsVisited = false;

    }

    }

       

       

    bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //对于有向图的弧设定,只需设定行和列,并在相应位置设定权值即可

    m_pMatrix[row*m_iCapacity + col] = val;

    return true;

    }

       

       

    bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //对于无向图的边设定,不仅要设定行和列,在相应位置设定权值,

    //还要设定当前指定行和列的主对角线的对称位置的权值

    m_pMatrix[row*m_iCapacity + col] = val;

    m_pMatrix[col*m_iCapacity + row] = val;

    return true;

    }

       

       

    bool CMap::getValueFromMatrix(int row, int col, int &val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //指定相应的行和列,查看邻接矩阵的相应位置的值

    //如果为0,就说明当前的行和列所表示的顶点不相连,

    //如果不为0,就说明当前的行和列所表示的顶点相连,

    //至于它们相连的权值由邻接矩阵取得

    val = m_pMatrix[row*m_iCapacity + col];

    return true;

    }

       

       

    void CMap::printMatrix()

    {

    for (int i = 0; i < m_iCapacity; i++)

    {

    for (int k = 0; k < m_iCapacity; k++)

    {

    cout << m_pMatrix[i*m_iCapacity + k] << " ";

    }

    cout << endl;

    }

       

    }

       

       

    //深度优先遍历:

    //图的深度优先遍历和二叉树的前序遍历非常相似

    //首先是访问根,再访问左子树,最后访问右子树,

    //对于图来说,做深度优先遍历,要先访问根,然

    //后访问第一棵子树,再访问第二棵子树,再访问

    //第三棵子树...一直将所有子树访问结束之后,整

    //个深度优先遍历就算结束了

    //

    //深度优先遍历的特点:

    //将当前这个节点所连接的所有延展节点全部访问完毕之后,

    //才回到根,然后再从根往下延展第二棵子树,以此类推

    void CMap::depthFirstTraverse(int nodeIndex)

    {

    //先访问当前传入进来的这个节点

    cout << m_pNodeArray[nodeIndex].m_cData << " ";

    //将当前节点设置为已经访问过了

    m_pNodeArray[nodeIndex].m_bIsVisited = true;

       

    //定义一个value,将来从邻接矩阵中取值进行判断

    int value = 0;

    //通过邻接矩阵判断当前这个顶点是否与其它顶点有连接

    for (int i = 0; i < m_iCapacity; i++)

    {

    //因为是无向图,所以nodeIndex可以作为第一个

    //参数,也可以作为第二个参数

    getValueFromMatrix(nodeIndex,i,value);

    //如果取出的value值不为0,表明有其它顶点和当前顶点连接

    if (value != 0)

    {

    //如果相连接的其它顶点已经被访问过,也pass

    //进入到下一次循环中

    if (m_pNodeArray[i].m_bIsVisited)

    {

    continue;

    }

    else

    {

    //如果没有被访问过,就进行递归

    depthFirstTraverse(i);

    }

    }

    else

    {

    //如果等于0,即不存在其它顶点和当前顶点相连

    continue;

    }

    }

    }

       

       

    //广度优先遍历:先给图进行分层,每一层都放到一个

    //单独的数组中,然后一层一层的进行遍历

    void CMap::breadthFirstTraverse(int nodeIndex)

    {

    //先访问当前传入进来的这个节点

    cout << m_pNodeArray[nodeIndex].m_cData << " ";

    //将当前节点设置为已经访问过了

    m_pNodeArray[nodeIndex].m_bIsVisited = true;

    //将当前节点保存到一个数组中,保存的不是

    //节点本身,只保存索引即可

    //这个数组使用标准模板库中的vector

    vector<int> curVec;

    curVec.push_back(nodeIndex);

    //调用实现函数来做进一步的遍历操作

    breadthFirstTraverseImpl(curVec);

    }

       

       

    void CMap::breadthFirstTraverseImpl(vector<int> preVec)

    {

    //定义一个value,将来从邻接矩阵中取值进行判断

    int value = 0;

    //再定义一个数组来保存当前这一层的所有节点

    vector<int> curVec;

       

       

    //遍历上一层数组中保存的节点

    //preVec.size()的返回值是size_type类型的,需强转为int

    for (int j = 0; j < (int)preVec.size(); j++)

    {

    //判断当前节点是否与其它节点是否有连接

    for (int i = 0; i < m_iCapacity; i++)

    {

    getValueFromMatrix(preVec[j],i,value);

    //如果不等于0,则有连接

    if (value != 0)

    {

    //如果相连接的点已经被访问过,就pass

    if (m_pNodeArray[i].m_bIsVisited)

    {

    continue;

    }

    else

    {

    //如果没有被访问过,就先访问一下

    cout << m_pNodeArray[i].m_cData << " ";

    //访问后,将其置为已访问过

    m_pNodeArray[i].m_bIsVisited = true;

    //将这个节点的索引保存到curVec

    curVec.push_back(i);

    }

    }

    }

    }

       

    //for循环全部完成,就意味着已经将preVec这一层顶点

    //的下一层顶点全部都访问完了

    //

    //判断本层是否存在刚被访问过的节点

    //如果本层没有,那下边的层次就更不存在了

    if (curVec.size() == 0)

    {

    return;

    }

    else

    {

    //如果本层还有节点,那下面的层次可能也

    //还有节点,进行递归

    breadthFirstTraverseImpl(curVec);

    }

    }

       

       

       

    main.cpp:

       

    #include "CMap.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

       

    /*

    图的存储:使用邻接矩阵实现

         A

        /

       B    D

      /   /

      C  F  G - H

       /

       E

    */

       

    /*

    8 个节点:分别是 A B C D E F G H

    其中:A B D 都相连,B C F 都相连,

    C F E 都相连,D G H 这三个节点都相连。

    显然,它是一个无向图,且每条边没有加权值,

    所以认为边的权值都为 1

    无向图的邻接矩阵的主对角线是对称线,即上

    半部分和下半部分是完全对称的

    */

       

    /*

    图的遍历:

    如果是从A点开始搜索:

    深度优先搜索,顺序:A B C E F D G H

    广度优先搜索,顺序:A B D C F G H E

    无论是深度优先搜索,还是广度优先搜索,它搜索的层次

    都是不确定的,可以通过递归的方式来解决这样的问题

    */

    int main(void)

    {

    CMap *pMap = new CMap(8);

       

    Node nodeA('A');

    Node nodeB('B');

    Node nodeC('C');

    Node nodeD('D');

    Node nodeE('E');

    Node nodeF('F');

    Node nodeG('G');

    Node nodeH('H');

       

    //按照添加的顺序自动设定索引值

    pMap->addNode(&nodeA);//0

    pMap->addNode(&nodeB);//1

    pMap->addNode(&nodeC);//2

    pMap->addNode(&nodeD);//3

    pMap->addNode(&nodeE);//4

    pMap->addNode(&nodeF);//5

    pMap->addNode(&nodeG);//6

    pMap->addNode(&nodeH);//7

    //只需设定无向图的主对角线的上半部分即可,且默认权值都为1

    //这里共设定9条边,设定的顺序不限

    pMap->setValueToMatrixForUndirectedGraph(0, 1);

    pMap->setValueToMatrixForUndirectedGraph(0, 3);

    pMap->setValueToMatrixForUndirectedGraph(1, 2);

    pMap->setValueToMatrixForUndirectedGraph(1, 5);

    pMap->setValueToMatrixForUndirectedGraph(3, 6);

    pMap->setValueToMatrixForUndirectedGraph(3, 7);

    pMap->setValueToMatrixForUndirectedGraph(6, 7);

    pMap->setValueToMatrixForUndirectedGraph(2, 4);

    pMap->setValueToMatrixForUndirectedGraph(4, 5);

       

       

    pMap->printMatrix();

    cout << endl;

       

       

    pMap->depthFirstTraverse(0);

    cout << endl;

       

    //在进行广度优先遍历之前,需要先重置一下,

    //因为之前深度优先遍历后,所有节点都已经

    //标识为被访问过

    pMap->resetNode();

    pMap->breadthFirstTraverse(0);

    cout << endl;

       

    delete pMap;

    pMap = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

       

    程序 2:

       

    1)使用邻接矩阵存储无向图

       

    2)使用普里姆(Prime)算法和克鲁斯卡尔(Kruskal)算法求最小生成树

       

       

    Node.h:

       

    #ifndef NODE_H

    #define NODE_H

       

    class Node

    {

    public:

    //默认有参构造函数

    Node(char data = 0);

       

    char m_cData;

       

    //标识当前节点有没有被访问过

    //被访问过就置为true

    bool m_bIsVisited;

    };

       

       

    #endif

       

       

       

    Node.cpp:

       

    #include "Node.h"

       

    Node::Node(char data)

    {

    m_cData = data;

    m_bIsVisited = false;

    }

       

       

       

    Edge.h:

       

    #ifndef EDGE_H

    #define EDGE_H

       

    //不论是普里姆算法,还是克鲁斯卡尔算法,

    //它们所涉及的边的数据成员和成员函数大抵相似

    class Edge

    {

    public:

    //默认有参构造函数

    Edge(int nodeIndexA = 0, int nodeIndexB = 0, int weightValue = 0);

       

    //连接这条边的两个顶点,并不需要强调它的起始和终结,

    //因为这里做的是无向图,并不是弧,而是一条边

    //所以就平等的写成 A B 这两个点

    int m_iNodeIndexA;

    int m_iNodeIndexB;

       

    //这条边的权值

    int m_iWeightValue;

       

    //标记这条边是否从待选边集合中被选择出来了

    bool m_bSelected;

    };

       

    #endif

       

       

       

    Edge.cpp:

       

    #include "Edge.h"

       

    Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue)

    {

    m_iNodeIndexA = nodeIndexA;

    m_iNodeIndexB = nodeIndexB;

    m_iWeightValue = weightValue;

    m_bSelected = false;

    }

       

       

       

    CMap.h:

       

    #ifndef CMAP_H

    #define CMAP_H

    #include "Node.h"

    #include "Edge.h"

    #include <vector>

    using namespace std;

       

       

    //之所以在Map前加个C,是因为Map本身是标准模板库的类名,

    //为了能够区分,就在前面加了一个C,叫做CMap

    class CMap

    {

    public:

    CMap(int capacity);

    ~CMap();

    bool addNode(Node *pNode); //向图中加入顶点(节点)

    void resetNode(); //重置顶点:将每一个顶点设为未访问过

       

    //为有向图设置邻接矩阵,默认权值为1

    bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1);

    //为无向图设置邻接矩阵,默认权值为1

    bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1);

       

    void printMatrix(); //打印邻接矩阵

    void depthFirstTraverse(int nodeIndex); //深度优先遍历

    void breadthFirstTraverse(int nodeIndex); //广度优先遍历

    void primTree(int nodeIndex); //普里姆算法生成树

    void kruskalTree(int nodeIndex); //克鲁斯卡尔算法生成树

       

    private:

    //从矩阵中获取权值(该函数不是公共使用的,而是内部使用的)

    bool getValueFromMatrix(int row, int col, int &val);

    //广度优先遍历实现函数

    void breadthFirstTraverseImpl(vector<int> preVec);

       

    //获取最小边

    int getMinEdge(vector<Edge> edgeVec);

    //判断顶点是否在点集合上

    bool isInSet(vector<int> nodeSet, int target);

    //合并两个顶点集合

    void mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB);

       

    private:

    int m_iCapacity; //图中最多可以容纳的顶点数

    int m_iNodeCount; //已经添加的顶点(节点)个数

    Node *m_pNodeArray; //用来存放顶点数组

    int *m_pMatrix; //用来存放邻接矩阵

    Edge *m_pEdge; //用来存放最小生成树中的边

    };

       

       

    #endif

       

       

       

    CMap.cpp:

       

    #include "CMap.h"

    #include <vector>

    #include <iostream>

    using namespace std;

       

       

    CMap::CMap(int capacity)

    {

    m_iCapacity = capacity;

    m_iNodeCount = 0;

    m_pNodeArray = new Node[m_iCapacity];

    //邻接矩阵--二维数组(实际上是用一维数组来表示二维数组)

    m_pMatrix = new int[m_iCapacity*m_iCapacity];

    //memset()函数:进行内存的设定

    //初始化邻接矩阵,将当前二维数组中的每一个元素都置为0

    memset(m_pMatrix, 0, m_iCapacity*m_iCapacity*sizeof(int));

       

    ////也可以采用for循环来初始化邻接矩阵

    //for (int i = 0; i < m_iCapacity*m_iCapacity; i++)

    //{

    // m_pMatrix[i] = 0;

    //}

       

    //最小生成树中的边的个数是顶点个数减1

    m_pEdge = new Edge[m_iCapacity - 1];

    }

       

       

    CMap::~CMap()

    {

    delete[]m_pNodeArray;

    m_pNodeArray = NULL;

    delete[]m_pMatrix;

    m_pMatrix = NULL;

    delete []m_pEdge;

    m_pEdge = NULL;

    }

       

       

    bool CMap::addNode(Node *pNode)

    {

    //判断传入进来的节点是否为空

    if (NULL == pNode)

    {

    return false;

    }

    //顶点索引就是m_pNodeArray这个数组的下标

    m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;

    m_iNodeCount++;

    return true;

    }

       

       

    //将图中所有节点的访问标识置为false

    void CMap::resetNode()

    { //将图中已经存放的所有顶点设为未被访问过

    //不管之前是否被访问过

    for (int i = 0; i < m_iNodeCount; i++)

    {

    m_pNodeArray[i].m_bIsVisited = false;

    }

    }

       

       

    bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //对于有向图的弧设定,只需设定行和列,并在相应位置设定权值即可

    m_pMatrix[row*m_iCapacity + col] = val;

    return true;

    }

       

       

    bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //对于无向图的边设定,不仅要设定行和列,在相应位置设定权值,

    //还要设定当前指定行和列的主对角线的对称位置的权值

    m_pMatrix[row*m_iCapacity + col] = val;

    m_pMatrix[col*m_iCapacity + row] = val;

    return true;

    }

       

       

    bool CMap::getValueFromMatrix(int row, int col, int &val)

    {

    if (row < 0 || row >= m_iCapacity)

    {

    return false;

    }

    if (col < 0 || col >= m_iCapacity)

    {

    return false;

    }

    //指定相应的行和列,查看邻接矩阵的相应位置的值

    //如果为0,就说明当前的行和列所表示的顶点不相连,

    //如果不为0,就说明当前的行和列所表示的顶点相连,

    //至于它们相连的权值由邻接矩阵取得

    val = m_pMatrix[row*m_iCapacity + col];

    return true;

    }

       

       

    void CMap::printMatrix()

    {

    for (int i = 0; i < m_iCapacity; i++)

    {

    for (int k = 0; k < m_iCapacity; k++)

    {

    cout << m_pMatrix[i*m_iCapacity + k] << " ";

    }

    cout << endl;

    }

       

    }

       

       

    //深度优先遍历:

    //图的深度优先遍历和二叉树的前序遍历非常相似

    //首先是访问根,再访问左子树,最后访问右子树,

    //对于图来说,做深度优先遍历,要先访问根,然

    //后访问第一棵子树,再访问第二棵子树,再访问

    //第三棵子树...一直将所有子树访问结束之后,整

    //个深度优先遍历就算结束了

    //

    //深度优先遍历的特点:

    //将当前这个节点所连接的所有延展节点全部访问完毕之后,

    //才回到根,然后再从根往下延展第二棵子树,以此类推

    void CMap::depthFirstTraverse(int nodeIndex)

    {

    //先访问当前传入进来的这个节点

    cout << m_pNodeArray[nodeIndex].m_cData << " ";

    //将当前节点设置为已经访问过了

    m_pNodeArray[nodeIndex].m_bIsVisited = true;

       

    //定义一个value,将来从邻接矩阵中取值进行判断

    int value = 0;

    //通过邻接矩阵判断当前这个顶点是否与其它顶点有连接

    for (int i = 0; i < m_iCapacity; i++)

    {

    //因为是无向图,所以nodeIndex可以作为第一个

    //参数,也可以作为第二个参数

    getValueFromMatrix(nodeIndex, i, value);

    //如果取出的value值不为0,表明有其它顶点和当前顶点连接

    if (value !=0)

    {

    //如果相连接的其它顶点已经被访问过,也pass

    //进入到下一次循环中

    if (m_pNodeArray[i].m_bIsVisited)

    {

    continue;

    }

    else

    {

    //如果没有被访问过,就进行递归

    depthFirstTraverse(i);

    }

    }

    else

    {

    //如果等于0,即不存在其它顶点和当前顶点相连

    continue;

    }

    }

       

    }

       

       

    //广度优先遍历:先给图进行分层,每一层都放到一个

    //单独的数组中,然后一层一层的进行遍历

    void CMap::breadthFirstTraverse(int nodeIndex)

    {

    //先访问当前传入进来的这个节点

    cout << m_pNodeArray[nodeIndex].m_cData << " ";

    //将当前节点设置为已经访问过了

    m_pNodeArray[nodeIndex].m_bIsVisited = true;

    //将当前节点保存到一个数组中,保存的不是

    //节点本身,只保存索引即可

    //这个数组使用标准模板库中的vector

    vector<int> curVec;

    curVec.push_back(nodeIndex);

    //调用实现函数来做进一步的遍历操作

    breadthFirstTraverseImpl(curVec);

    }

       

       

    void CMap::breadthFirstTraverseImpl(vector<int> preVec)

    {

    //定义一个value,将来从邻接矩阵中取值进行判断

    int value = 0;

    //再定义一个数组来保存当前这一层的所有节点

    vector<int> curVec;

       

       

    //遍历上一层数组中保存的节点

    //preVec.size()的返回值是size_type类型的,需强转为int

    //(其实转不转换类型都无所谓)

    for (int j = 0; j < (int)preVec.size(); j++)

    {

    //判断当前节点是否与其它节点是否有连接

    for (int i = 0; i < m_iCapacity; i++)

    {

    getValueFromMatrix(preVec[j], i, value);

    //如果不等于0,则有连接

    if (value != 0)

    {

    //如果相连接的点已经被访问过,就pass

    if (m_pNodeArray[i].m_bIsVisited)

    {

    continue;

    }

    else

    {

    //如果没有被访问过,就先访问一下

    cout << m_pNodeArray[i].m_cData << " ";

    //访问后,将其置为已访问过

    m_pNodeArray[i].m_bIsVisited = true;

    //将这个节点的索引保存到curVec

    curVec.push_back(i);

    }

    }

    }

    }

       

    //for循环全部完成,就意味着已经将preVec这一层顶点

    //的下一层顶点全部都访问完了

    //

    //判断本层是否存在刚被访问过的节点

    //如果本层没有,那下边的层次就更不存在了

    if (curVec.size() == 0)

    {

    return;

    }

    else

    {

    //如果本层还有节点,那下面的层次可能也

    //还有节点,进行递归

    breadthFirstTraverseImpl(curVec);

    }

    }

       

       

    //普里姆算法是以点作为算法的核心

    void CMap::primTree(int nodeIndex)

    {

    //取边,把边的权值保存到value

    int value = 0;

    //边的计数器

    int edgeCount = 0;

    //数组nodeVec用来存放节点(点集合)

    //这里实际存放的是点的索引

    vector<int> nodeVec;

    //数组edgeVec用来存放待选边(待选边集合)

    vector<Edge> edgeVec;

       

    cout << m_pNodeArray[nodeIndex].m_cData << endl;

    nodeVec.push_back(nodeIndex);

    m_pNodeArray[nodeIndex].m_bIsVisited = true;

    //算法的终结条件:

    //对于普里姆算法来说,最终拿到的边数如果已经

    //等于当前点数减1,那么就到达了可以停止的条件

    while (edgeCount < m_iCapacity - 1)

    {

    //先从nodeVec取出nodeIndex对应的点,拿着

    //这个点把边找出来

    //

    //nodeVec.back() 即从nodeVec中取出最尾部

    //的那个元素,也就是nodeIndex

    int temp = nodeVec.back();

       

    //for循环将所有与temp相连接的边都放入待选边集合中

    for (int i = 0; i < m_iCapacity; i++)

    {

    getValueFromMatrix(temp, i, value);

    //如果权值不为0,则表明当前节点和其它节点有连接

    if (value != 0)

    {

    //如果被访问过,就pass

    if (m_pNodeArray[i].m_bIsVisited)

    {

    continue;

    }

    else

    {

    //如果没有被访问过,则这条边应该放入到待选边集合中

    Edge edge(temp, i, value);

    edgeVec.push_back(edge);

    }

    }

    }

       

    //从待选边集合中找到权值最小的边,同时把这条边

    //的另一个顶点放入点集合中

    int edgeIndex = getMinEdge(edgeVec);

    //将这条边标记为被选出

    edgeVec[edgeIndex].m_bSelected = true;

       

    cout << edgeVec[edgeIndex].m_iNodeIndexA << "->"

    << edgeVec[edgeIndex].m_iNodeIndexB << " #";

    cout << edgeVec[edgeIndex].m_iWeightValue << endl;

       

    //将这条边的索引存储到边集合中

    m_pEdge[edgeCount] = edgeVec[edgeIndex];

    edgeCount++;

       

    //找到权值最小边的另一个顶点,作为下次循环时

    //找与这顶点相连的边时使用

    int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;

    //并将这个点放入到点集合中

    nodeVec.push_back(nextNodeIndex);

    //将这个点设为已被访问过

    m_pNodeArray[nextNodeIndex].m_bIsVisited = true;

       

    cout << m_pNodeArray[nextNodeIndex].m_cData << endl;

       

    }

    }

       

       

    int CMap::getMinEdge(vector<Edge> edgeVec)

    {

    //最小权值

    int minWeight = 0;

    //边的索引

    int edgeIndex = 0;

    int i = 0;

    //找出第一条没有被选中的边

    for (; i < (int)edgeVec.size(); i++)

    {

    //如果这条边没有被选出来过

    if (!edgeVec[i].m_bSelected)

    {

    //将权值赋值给minWeight

    minWeight = edgeVec[i].m_iWeightValue;

    edgeIndex = i;

    break;

    }

    }

       

       

    if (minWeight == 0)

    {

    return -1;

    }

       

       

    //拿着第一条没有被选中的边的权值去和

    //其它没有被选中的边的权值进行比较

    //找出最小权值的边

    for (; i < (int)edgeVec.size(); i++)

    {

    //如果这条边被选出来过

    if (edgeVec[i].m_bSelected)

    {

    continue;

    }

    else

    {

    if (minWeight>edgeVec[i].m_iWeightValue)

    {

    minWeight = edgeVec[i].m_iWeightValue;

    edgeIndex = i;

    }

    }

    }

       

    return edgeIndex;

    }

       

       

    //克鲁斯卡尔算法是以边作为算法的核心,它最终是要找出

    //一个边的集合,让这个边的集合能够形成一棵最小生成树

    //

    //需要分成两步来走:

    //第一步:找出所有的边,

    //第二步:从这些边中找出组成最小生成树的边集合

    //1)找到算法的结束条件

    //2)从边集合中找到最小边

    //3)找出最小边连接的点

    //4)找出点所在的点集合

    //5)根据点所在集合的不同做出不同处理

    void CMap::kruskalTree(int nodeIndex)

    {

    //用来取边的权值的value

    int value = 0;

    //取出来的最小生成树的边的个数

    int edgeCount = 0;

    //定义存放已涉及点的数组,也就是数组的数组

    //这样定义的原因是:克鲁斯卡尔算法的点集合

    //有可能不止一个,就拿本例来说,将来:A F B

    //C 会在一个集合,而 D E 在另一个集合,然后

    //再把这两个集合合并成一个集合

    vector<vector<int>> nodeSets;

    //存放待选边的数组

    vector<Edge> edgeVec;

    //通过for循环将邻接矩阵中所有的关于边的表达

    //都把它变成一个Edge对象,放入到edgeVec数组中

    for (int i = 0; i < m_iCapacity; i++)

    { //从邻接矩阵的上半部分取边(不含主对角线)

    for (int k = i + 1; k < m_iCapacity; k++)

    {

    getValueFromMatrix(i, k, value);

    if (value != 0)

    {

    Edge edge(i,k,value);

    edgeVec.push_back(edge);

    }

    }

    }

       

    while (edgeCount < m_iCapacity - 1)

    {

    int minEdgeIndex = getMinEdge(edgeVec);

    edgeVec[minEdgeIndex].m_bSelected = true;

       

    int nodeAIndex = edgeVec[minEdgeIndex].m_iNodeIndexA;

    int nodeBIndex = edgeVec[minEdgeIndex].m_iNodeIndexB;

       

    bool nodeAIsInSet = false;

    bool nodeBIsInSet = false;

       

    //保存nodeAnodeB所在集合的索引

    int nodeAInSetLabel = -1;

    int nodeBInSetLabel = -1;

       

    //分别判断nodeAIndexnodeBIndex分别在哪个点集合中

    for (int i = 0; i < (int)nodeSets.size(); i++)

    {

    nodeAIsInSet = isInSet(nodeSets[i],nodeAIndex);

    //如果nodeAIndexnodeSets[i]

    //注意:不可能有点同时存在于两个集合中

    if (nodeAIsInSet)

    {

    nodeAInSetLabel = i;

    }

       

    nodeBIsInSet = isInSet(nodeSets[i], nodeBIndex);

    if (nodeBIsInSet)

    {

    nodeBInSetLabel = i;

    }

    }

       

    //判断nodeAInSetLabel nodeBInSetLabel的关系:

    //

    //1)如果都为-1,即 A B 都不在已有集合中,

    //需要放到一个全新的集合中

    if (nodeAInSetLabel == -1 && nodeBInSetLabel == -1)

    {

    vector<int> vec;

    //显然,这是第一条最小权值的边,两个点都在同一集合中

    vec.push_back(nodeAIndex);

    vec.push_back(nodeBIndex);

    //再将集合放入到集合的集合中

    nodeSets.push_back(vec);

    }

    //2A-1B不为-1,即 A 没有在任何集合中,而 B 此时

    //是归属于某一集合的,需要把 A 放到 B 所在的集合中

    else if (nodeAInSetLabel == -1 && nodeBInSetLabel != -1)

    {

    nodeSets[nodeBInSetLabel].push_back(nodeAInSetLabel);

    }

    //3A不为-1B-1,同(2

    else if (nodeAInSetLabel != -1 && nodeBInSetLabel == -1)

    {

    nodeSets[nodeAInSetLabel].push_back(nodeBInSetLabel);

    }

    //4A B 分属于不同的集合,这说明要找的这条边是连接

    //到了两个集合中的不同点,就要合并两个集合

    else if (nodeAInSetLabel != -1 && nodeBInSetLabel != -1

    && nodeAInSetLabel != nodeBInSetLabel)

    {

    //B所在集合合并到前面的A所在集合

    mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel]);

    //合并之后,将 B 所在集合从nodeSets去掉

    for (int k = nodeBInSetLabel; k < (int)nodeSets.size()-1; k++)

    {

    nodeSets[k] = nodeSets[k + 1];

    }

    }

    //5A B 在同一集合中,这就使得已经选出来的边形成了

    //回路,这条边就要放弃掉,直接continue

    else if (nodeAInSetLabel != -1 && nodeBInSetLabel != -1

    && nodeAInSetLabel == nodeBInSetLabel)

    {

    continue;

    }

       

    m_pEdge[edgeCount] = edgeVec[minEdgeIndex];

    edgeCount++;

       

    cout << edgeVec[minEdgeIndex].m_iNodeIndexA << "->"

    << edgeVec[minEdgeIndex].m_iNodeIndexB << " #";

    cout << edgeVec[minEdgeIndex].m_iWeightValue << endl;

    }

    }

       

       

    bool CMap::isInSet(vector<int> nodeSet, int target)

    {

    for (int i = 0; i < (int)nodeSet.size(); i++)

    {

    if (nodeSet[i] == target)

    {

    return true;

    }

    }

       

    return false;

    }

       

       

    //nodeSetB合并到nodeSetA,使A这个集合壮大起来,之所以将nodeSetA加引用,

    //就是为了将来传进来的nodeSetA集合,未来还能把结果拿出去

    void CMap::mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB)

    {

    //nodeSetB中的每一个点取出来放到A点集合的后边即可

    for (int i = 0; i < (int)nodeSetB.size(); i++)

    {

    nodeSetA.push_back(nodeSetB[i]);

    }

    }

       

       

       

    main.cpp:

       

    #include "CMap.h"

    #include "stdlib.h"

    #include <iostream>

    using namespace std;

       

       

    /*

          A

        /  |  

       B---F---E

        / /

        C---D

    这张图中共6个顶点,并有编号:

    A B C D E F

    0 1 2 3 4 5

    而且边和边之间有一定的权值,共10条边:

    A-B 6 A-E 5 A-F 1

    B-C 3 B-F 2

    C-F 8 C-D 7

    D-F 4 D-E 2

    E-F 9

    */

    int main(void)

    {

    CMap *pMap = new CMap(6);

       

    Node nodeA('A');

    Node nodeB('B');

    Node nodeC('C');

    Node nodeD('D');

    Node nodeE('E');

    Node nodeF('F');

       

       

    //按照添加的顺序自动设定索引值

    pMap->addNode(&nodeA);//0

    pMap->addNode(&nodeB);//1

    pMap->addNode(&nodeC);//2

    pMap->addNode(&nodeD);//3

    pMap->addNode(&nodeE);//4

    pMap->addNode(&nodeF);//5

       

    //只需设定无向图的主对角线的上半部分即可,

    //这里共设定10条边,设定的顺序不限

    pMap->setValueToMatrixForUndirectedGraph(0, 1, 6);//A-B

    pMap->setValueToMatrixForUndirectedGraph(0, 4, 5);//A-E

    pMap->setValueToMatrixForUndirectedGraph(0, 5, 1);//A-F*

    pMap->setValueToMatrixForUndirectedGraph(1, 2, 3);//B-C*

    pMap->setValueToMatrixForUndirectedGraph(1, 5, 2);//B-F*

    pMap->setValueToMatrixForUndirectedGraph(2, 5, 8);//C-F

    pMap->setValueToMatrixForUndirectedGraph(2, 3, 7);//C-D

    pMap->setValueToMatrixForUndirectedGraph(3, 5, 4);//D-F*

    pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);//D-E*

    pMap->setValueToMatrixForUndirectedGraph(4, 5, 9);//E-F

    pMap->primTree(0);

    cout << endl << endl;

    pMap->resetNode();

    pMap->kruskalTree(0);

       

    delete pMap;

    pMap = NULL;

       

    system("pause");

    return 0;

    }

       

       

    运行一览:

       

       

       

       

       

       

       

       

       

       

       

       

       

    【made by siwuxie095】

  • 相关阅读:
    [Redis知识体系] 一文全面总结Redis知识体系
    MySQL数据导入到ClickHouse
    docker本地搭建clickhouse
    【linux】修改宝塔默认的PHP CLI版本
    windows 10 安装go环境
    docker安装centos8
    Bootstrap 简洁、直观、强悍的前端开发框架,让web开发更迅速、简单。
    C#调用WebService
    登录时,记住用户的帐号密码
    asp.net,cookie,写cookie,取cookie
  • 原文地址:https://www.cnblogs.com/siwuxie095/p/6850689.html
Copyright © 2011-2022 走看看