在图中,有很多种算法,比如最常见的P算法和K算法。这里介绍一下P算法寻找最短路径算法:
1.任意找一个索引的点,通常是索引为0的第一个节点。输出这个节点数据,向容器中添加一个节点,并标记为已经被访问过。
2.设置几个变量,value(记录边的权值),edgeCount(记录加入到边容器里的边数),temp(记录点容器加入值的下标,当成传入参数获取边上权值)。
3.下面的两个函数是最核心的代码,一个是;
//普里姆核心算法实现(P算法)
void CMap::PrimTree(int nodeIndex)
{
int value = 0;
int edgeCount = 0;
vector<int> nodeVec;//节点集合
vector<Edge>edgeVec;//边的集合
cout << m_pNodeArray[nodeIndex].m_cData << endl;//输出第一个节点,也就是所谓的0索引节点
nodeVec.push_back(nodeIndex);//往容器中添加一个节点。
m_pNodeArray[nodeIndex].m_bIsVisited = true;//表示节点被访问过。
while (edgeCount < m_iCapacity - 1)//最小生成树中m_iCapacity个节点,那么会有m_iCapacity-1个边。
{
int temp = nodeVec.back();//取出vector中的最后一个元素。也就是上一次循环加入的新的节点//end() 函数返回一个指向当前vector末尾元素的下一位置的迭代器.要访问末尾元素,需要先将此迭代器减1。
// 并用参数temp存储 // back() 函数返回当前vector最末一个元素的引用。
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(temp, i, value);//获取边上的权值
if (value != 0)//取到了权值
{
if (m_pNodeArray[i].m_bIsVisited == true)//判断与最开始节点相连的节点标记为访问过
{
continue;//被访问则继续下一次循环继续找边
}
else
{
Edge edge(temp, i, value);//新建边
edgeVec.push_back(edge);//将边放入边容器内
}
}
}
//从可选边集合中找出最小的边
int edgeIndex = getMinEdge(edgeVec);//这个函数的实现也很关键,找到了最小权值边的索引,后面在此基础上继续
edgeVec[edgeIndex].m_bSelected = true;//定义edge类时候,里面的相关成员这里全部就开始被用上了
cout << edgeVec[edgeIndex].m_iNodeIndexA << "----" << edgeVec[edgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[edgeIndex].m_iWeightValue << endl;//这里输出 ,边,权值
//m_pEdge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;//这个变量的值一直增长到和m_iCapacity-1相等就退出了while(1)循环
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex);//与上面的nodeVec.back()对应,放入边的另外一个节点
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 < edgeVec.size(); i++)//遍历边容器中的所有边
{
if (edgeVec[i].m_bSelected == false)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
break;
}
}
if (0 == minWeight)
{
return -1;
}
for (; i < 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;//返回边的索引值
}
以上是实现算法的核心函数,里面有注释。
下面给出整个工程代码:分为Node.h和Node.cpp,Edge.h和Edge.cpp,和CMap.h和CMap.cpp,最后加上Demo.cpp中的主函数调用代码。
#ifndef Node_h
#define Node_h
class Node
{
public:
Node();//这是默认构造函数,必须要有
Node(char data);//这个是对节点的初始化构造函数
~Node();
public:
char m_cData;//定义了字符型数据
bool m_bIsVisited;//判断当前节点是否被访问
};
#endif
#include "Node.h"
Node::Node()
{
}
Node::Node(char data)
{
m_cData = data;
m_bIsVisited = false;
}
Node::~Node()
{
}
#ifndef Edge_h_
#define Edge_h_
class Edge//这个类是边,里面的成员变量就都是边的要素(两个点,边的权值,边是否标记变量)
{
public:
Edge(int NodeIndexA = 0, int NodeIndexB = 0,int WeightValue = 0);
int m_iNodeIndexA;
int m_iNodeIndexB;//A.B两个节点
int m_iWeightValue;
bool m_bSelected;
};
#endif
#include "Edge.h"
Edge::Edge(int NodeIndexA , int NodeIndexB , int WeightValue )
{
m_iNodeIndexA = NodeIndexA;
m_iNodeIndexB = NodeIndexB;
m_iWeightValue = WeightValue;
m_bSelected = false;
}
#ifndef Cmap_h
#define Cmap_h
#include "Node.h"
#include "Edge.h"
#include<vector>
using namespace std;
//#include<vector.h>
class CMap
{
public:
CMap(int capacity);//capacity表示图中可以容纳节点的个数
~CMap();
bool addNode(Node* pNode);//增加节点进入图
void resetNode();//将所有节点重置为没有被访问过
bool setValueToMatrixForDirectedGeaph(int row, int col, int val = 1);//有向图设置邻接矩阵,val为权值,这里默认值为1
bool setValueToMatrixForUndirectedGeaph(int row, int col, int val = 1);//无向图设置临界矩阵
void printMatrix();//打印出邻接矩阵
void depthFirstTraverse(int nodeIndex);//深度遍历图的节点
//void breadthFirstTravese(int nodeIndex);
public:
void PrimTree(int nodeIndex); //p算法产生最小生成树
private:
bool getValueFromMatrix(int row, int col, int&val);
//void breadthFirstTraveseImp(vector<int>preVec);
int getMinEdge(vector<Edge>edgeVec);//获取最短边
private:
int m_iCapacity;//图中最多可以容纳的定点数
int m_iNodeCount;//已经添加的节点个数
Node* m_pNodeArray;//用来存放定点数组
int *m_pMatrix;//和邻接矩阵有关
Edge* m_pEdge;//这个指针是用来存放边(Edge)的,就是用指针指向一段Edge型的内存空间。来存放Edge。
};
#endif
#include "CMap.h"
#include "Edge.h"
//#include "Node.h"
#include<iostream>
#include<vector>
using namespace std;
CMap::CMap(int capacity)
{
m_iCapacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_iCapacity];//这里堆上分配的图节点数组用来存储外面传进来的节点数据,在addNode(Node* pNode)中可以看到关系
m_pMatrix = new int[m_iCapacity*m_iCapacity];//邻接矩阵中数据的存储数组
memset(m_pMatrix, 0, m_iCapacity*m_iCapacity*sizeof(int));//初始化邻接矩阵
//for (int i = 0; i < m_iCapacity*m_iCapacity; i++)
//{
// m_pMatrix[i] = 0;
//}
m_pEdge = new Edge[m_iCapacity-1];//分配存放Edge边的空间
}
CMap::~CMap()
{
delete[]m_pNodeArray;//节点数组
delete[]m_pMatrix;//邻接矩阵数组
}
/*
此为增加数据节点函数,节点内存在构造函数里已经被分配了。
这里只需要将pNode中的数据传入构造函数里分配的内存节点
*/
bool CMap::addNode(Node* pNode)
{
if (pNode == NULL)
{
return false;
}
m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData;
m_iNodeCount++;//没加一个节点,节点计数变量加一
return true;
}
/*
m_bIsVisited表示节点在遍历的时候是否被访问,这是个bool型的变量,被访问为true,没有被被访问为false。
这个函数就是将所有有的节点标志为未被访问过。
*/
void CMap::resetNode()
{
for (int i = 0; i < m_iNodeCount; i++)
{
m_pNodeArray[i].m_bIsVisited = false;
}
}
/*
函数名称:向有向图添加节点(注意是有向图,那么其邻接矩阵的值就不会关于对角线对称,有一个三角形就可以)
*/
bool CMap::setValueToMatrixForDirectedGeaph(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::setValueToMatrixForUndirectedGeaph(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;
}
/*
函数功能:打印出图的邻接矩阵
函数实现:两个for循环
*/
void CMap::printMatrix()
{
for (int i = 0; i < m_iCapacity; i++)
{
for (int j = 0; j < m_iCapacity; j++)
{
cout << m_pMatrix[i*m_iCapacity + j] << " ";
}
cout << endl;
}
}
/*
函数名称:从邻接矩阵中获取指定坐标值
*/
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;
}
val = m_pMatrix[row*m_iCapacity + col];
return true;
}
/*
函数功能:图的深度优先遍历
函数实现:传入参数nodeIndex表示从第几个节点开始遍历
*/
void CMap::depthFirstTraverse(int nodeIndex)
{
int value = 0;
cout << m_pNodeArray[nodeIndex].m_cData << " ";//先输出第一个节点的数据
m_pNodeArray[nodeIndex].m_bIsVisited = true;//将刚访问的节点标记为访问过
for (int i = 0; i < m_iCapacity; i++)//每一次循环 i 都会加1
{
getValueFromMatrix(nodeIndex, i, value);//获得邻接矩阵中的值,1代表有弧相连,0代表两个节点没有弧相互连接
if (value == 1)//有弧存在
{
if (m_pNodeArray[i].m_bIsVisited == true)//
{
continue;//节点被访问过,则i+1,进入
}
else
{
depthFirstTraverse(i);//这是核心的递归操作代码,这里要仔细对照邻接矩阵和图来理解,不同于广度优先遍历
}
}
else
{
continue;
}
}
}
/*
函数名称:广度优先算法
*/
//void CMap::breadthFirstTravese(int nodeIndex)
//{
//
//}
//void CMap::breadthFirstTraveseImp(vector<int>preVec)
//{
//
//}
//普里姆核心算法实现(P算法)
void CMap::PrimTree(int nodeIndex)
{
int value = 0;
int edgeCount = 0;
vector<int> nodeVec;//节点集合
vector<Edge>edgeVec;//边的集合
cout << m_pNodeArray[nodeIndex].m_cData << endl;//输出第一个节点,也就是所谓的0索引节点
nodeVec.push_back(nodeIndex);//往容器中添加一个节点。
m_pNodeArray[nodeIndex].m_bIsVisited = true;//表示节点被访问过。
while (edgeCount < m_iCapacity - 1)//最小生成树中m_iCapacity个节点,那么会有m_iCapacity-1个边。
{
int temp = nodeVec.back();//取出vector中的最后一个元素。也就是上一次循环加入的新的节点//end() 函数返回一个指向当前vector末尾元素的下一位置的迭代器.要访问末尾元素,需要先将此迭代器减1。
// 并用参数temp存储 // back() 函数返回当前vector最末一个元素的引用。
for (int i = 0; i < m_iCapacity; i++)
{
getValueFromMatrix(temp, i, value);//获取边上的权值
if (value != 0)//取到了权值
{
if (m_pNodeArray[i].m_bIsVisited == true)//判断与最开始节点相连的节点标记为访问过
{
continue;//被访问则继续下一次循环继续找边
}
else
{
Edge edge(temp, i, value);//新建边
edgeVec.push_back(edge);//将边放入边容器内
}
}
}
//从可选边集合中找出最小的边
int edgeIndex = getMinEdge(edgeVec);//这个函数的实现也很关键,找到了最小权值边的索引,后面在此基础上继续
edgeVec[edgeIndex].m_bSelected = true;//定义edge类时候,里面的相关成员这里全部就开始被用上了
cout << edgeVec[edgeIndex].m_iNodeIndexA << "----" << edgeVec[edgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[edgeIndex].m_iWeightValue << endl;//这里输出 ,边,权值
//m_pEdge[edgeCount] = edgeVec[edgeIndex];
edgeCount++;//这个变量的值一直增长到和m_iCapacity-1相等就退出了while(1)循环
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex);//与上面的nodeVec.back()对应,放入边的另外一个节点
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 < edgeVec.size(); i++)//遍历边容器中的所有边
{
if (edgeVec[i].m_bSelected == false)
{
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
break;
}
}
if (0 == minWeight)
{
return -1;
}
for (; i < 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;//返回边的索引值
}
最后的Demo代码
#include <iostream>
#include<stdlib.h>
#include "CMap.h"
using namespace std;
int main()
{
CMap* pMap = new CMap(6);//这里是建立一个CMap*型指针。来调用相关函数
Node* nodeA = new Node('A');//这里是创建8个节点。
Node* nodeB = new Node('B');
Node* nodeC = new Node('C');
Node* nodeD = new Node('D');
Node* nodeE = new Node('E');
Node* nodeF = new Node('F');
pMap->addNode(nodeA);//将节点增加到图中
pMap->addNode(nodeB);
pMap->addNode(nodeC);
pMap->addNode(nodeD);
pMap->addNode(nodeE);
pMap->addNode(nodeF);
/*pMap->addNode(nodeG);
pMap->addNode(nodeH);*/
pMap->setValueToMatrixForUndirectedGeaph(0, 1,6);//表示那几个节点相连,在临界矩阵中表现出来
pMap->setValueToMatrixForUndirectedGeaph(0, 4,5);
pMap->setValueToMatrixForUndirectedGeaph(0, 5,1);
pMap->setValueToMatrixForUndirectedGeaph(1, 2,3);
pMap->setValueToMatrixForUndirectedGeaph(1, 5,2);
pMap->setValueToMatrixForUndirectedGeaph(2, 5,8);
pMap->setValueToMatrixForUndirectedGeaph(2, 3,7);
pMap->setValueToMatrixForUndirectedGeaph(3, 5,4);
pMap->setValueToMatrixForUndirectedGeaph(3, 4,2);
pMap->setValueToMatrixForUndirectedGeaph(4, 5,9);
//pMap->printMatrix();
pMap->PrimTree(0);
cout << endl;
//pMap->depthFirstTraverse(0);
system("pause");
return 0;
}
仅供参考。