图 续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;
//保存nodeA和nodeB所在集合的索引
int nodeAInSetLabel = -1;
int nodeBInSetLabel = -1;
//分别判断nodeAIndex和nodeBIndex分别在哪个点集合中
for (int i =0; i < (int)nodeSets.size(); i++)
{
nodeAIsInSet = isInSet(nodeSets[i],nodeAIndex);
//如果nodeAIndex在nodeSets[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);
}
//(2)A为-1,B不为-1,即 A 没有在任何集合中,而 B此时
//是归属于某一集合的,需要把 A 放到 B所在的集合中
else if (nodeAInSetLabel == -1 && nodeBInSetLabel != -1)
{
nodeSets[nodeBInSetLabel].push_back(nodeAInSetLabel);
}
//(3)A不为-1,B为-1,同(2)
else if (nodeAInSetLabel != -1 && nodeBInSetLabel == -1)
{
nodeSets[nodeAInSetLabel].push_back(nodeBInSetLabel);
}
//(4)A和 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];
}
}
//(5)A和 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】
- 图 续3
- 图 续1
- 图 续2
- 话说《三字经》续3
- 数据结构(续3)
- mysql字符集续3
- Java 续3
- select 函数----续 3
- 3、模板开发续
- Servlet_09_ServletContext(续3)
- set 续3
- for 续3
- 续3:接上一篇
- 堆 续3
- 图3
- #图3
- Absolute C++心得(续3)
- 笔记3------ssh层次续
- 2.5>>迷宫
- java基础第一章_常量
- Oracle基本操作十一:游标
- 《数据压缩》实验报告四·DPCM编解码
- 拷贝构造与赋值运算符重载(顺序)
- 图 续3
- 我的笔记
- 两种方法实现语句反向
- 数据压缩实验4-DPCM编码
- 安全开发相关注意事项
- QT涂鸦窗口/写字板
- BCL(基本类库)和FCL(框架类库)
- linux下安装MySQL
- PAT 甲级1012. The Best Rank (25)