图 续3

来源:互联网 发布:中国广电网络缴费 编辑:程序博客网 时间:2024/05/22 11:08

------------------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 =newint[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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

  

程序 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 =newint[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");

return0;

}

  

  

运行一览:

  

  

  

  

  

  

  

  

  

  

  

  

  

【made by siwuxie095】

0 0
原创粉丝点击