图 - C++实现最短路径(单源、多源)附加:Prim实现最小生成树

代码中所对应的图:

最小生成树:


涉及到的知识:

1. Dijkstra算法。2. Floyd算法。3. 无向有权图的建立。4. Prim算法。



//use邻接矩阵存储的图的最短路径问题

#include <iostream>
#include <malloc.h>
using namespace std;

const int ERROR = -100;  //错误标记
const int MaxVertexNum = 100; //最大顶点数
const int MAXNUM = 65535;  //∞
typedef int Vertex;     //用顶点下标表示顶点,为整型
typedef int WeightType;       //边的权值设为整型
typedef char DataType;   //顶点存储的数据类型设为字符型

int Visited[MaxVertexNum];    //Visited[]为全局变量,用来标记节点是否被访问
int dist[MaxVertexNum];     //图中各个顶点到源点的最短路径
int p[MaxVertexNum][MaxVertexNum];  //记录Floyd问题中的路径


									//MGraph类的定义 - 邻接矩阵表示的图
struct
{
	int num;
	int pnode[MaxVertexNum];
}path[MaxVertexNum];       //path为从V0到各顶点的最短路径

						   //边的定义
typedef struct ENode *PtrToENode;
struct ENode
{
	Vertex V1, V2;     //有向边<v1,v2>
	WeightType Weight;   //权重
};
typedef PtrToENode Edge;



//MGraph类的定义
template <class T>
class MGraph
{
private:
	int Nv;  //顶点数
	int Ne;  //边数
	T G[MaxVertexNum][MaxVertexNum];  //邻接矩阵

public:
	MGraph() { Nv = 0; Ne = 0; }  //构造函数
								  //	~MGraph();  //析构函数
	bool BuildGraph();  //根据用户输入建立一个图
	bool Dijkstra(Vertex s);  //Dijkstra算法求最短路径
	void DisDijPath();  //输出单源最短路径的result
	Vertex FindMinDist(int collected[]); //返回未被收录顶点中dist最小值
	bool Floyd();  //Floyd算法求解多源最短路径
	void PrintFloyd();  //output多源最短路径的result
	void Prim(MGraph& PrimTree);  //最小生成树算法
	void DispPrim();  //输出最小生成树
};

//MGraph类的实现
//BuildGraph() - 根据用户输入建立一个图
template <class T>
bool MGraph<T>::BuildGraph()
{
	cout << "顶点数:";
	cin >> Nv;

	//初始化有Nv个顶点但是没有边的图
	//初始化邻接矩阵
	//注意:这里默认顶点编号从0开始,到(Graph->Nv-1)
	Vertex V, W;
	for (V = 0; V < Nv; V++)
		for (W = 0; W < Nv; W++)
			G[V][W] = MAXNUM;

	cout << "边数:";
	cin >> Ne;
	Edge E;
	int i;
	if (Ne)  //如果有边
	{
		E = (Edge)malloc(sizeof(struct ENode));  //建立边节点
												 //读入边,格式为"起点、终点、权重",并插入邻接矩阵
		cout << "以起点、终点、权重的格式读入边" << endl;
		for (i = 0; i < Ne; i++)
		{
			cin >> E->V1 >> E->V2 >> E->Weight;

			//插入边<v1,v2>
			G[E->V1][E->V2] = E->Weight;
			//若是无向图,还要插入边<v2,v1>
			G[E->V2][E->V1] = E->Weight;
		}
	}
}

//FindMinDist()返回未被收录顶点中的dist最小值
template <class T>
Vertex MGraph<T>::FindMinDist(int collected[])
{
	Vertex MinV, V;
	int MinDist = MAXNUM;

	for (V = 0; V < Nv; V++)
		//若V未被收录,且dist[V]更小
		if (collected[V] == false && dist[V] < MinDist)
		{
			MinDist = dist[V];  //更新最小距离
			MinV = V;   //更新对应顶点
		}
	if (MinDist < MAXNUM)  //若找到最小dist
		return MinV;  //返回对应的顶点下标
	else
		return ERROR;  //若这样的顶点不存在,则返回错误标记
}


//Dijkstra算法 - 单源最短路径
template <class T>
bool MGraph<T>::Dijkstra(Vertex S)
{
	int collected[MaxVertexNum];
	Vertex V, W;

	//初始化:此处默认邻接矩阵中不存在边用MAXNUM表示
	for (V = 0; V < Nv; V++)
	{
		dist[V] = G[S][V];
		path[V].pnode[0] = S;
		path[V].num = 0;
		collected[V] = false;
	}

	//先将起点收入集合
	dist[S] = 0;
	collected[S] = true;

	while (1)
	{
		//V = 未被收录的顶点中dist最小者
		V = FindMinDist(collected);
		if (V == ERROR)  //若这样的V不存在
			break;  //算法结束
		collected[V] = true;  //收录V
		for (W = 1; W < Nv; W++)  //对于图中的每个顶点W
								  //若W是V的邻接点并且未被收录
			if (collected[W] == false && G[V][W] < MAXNUM)
			{
				if (G[V][W] < 0) //若有负边
					return false;  //不能正确解决,返回错误标记
								   //若收录V使得dist[W]变小
				if (dist[V] + G[V][W] < dist[W])
				{
					dist[W] = dist[V] + G[V][W];  //更新dist[W]
					path[W].num++;  //更新S到W的路径 
					path[W].pnode[path[W].num] = V;
				}
			}

		path[V].num++;   //将终点V加到路径中
		path[V].pnode[path[V].num] = V;
	}

	return true;  //算法执行完毕,返回正确标记
}

//DisDijPath() - display单源最短路径result
template <class T>
void MGraph<T>::DisDijPath()
{
	int i, j;

	cout << endl;
	cout << "\t从V0到各个顶点的最短路径长度如下" << endl;
	cout << "\t(起点->终点)  最短长度    最短路径" << endl;
	cout << "\t------------  -------     --------" << endl;
	for (i = 1; i < Nv; i++)
	{
		cout << "\t (V0->V" << i << "):   ";
		if (dist[i] < MAXNUM)
			cout << "    " << dist[i] << "        (";
		else
			cout << "∞     (";

		//	cout << endl;
		for (j = 0; j < path[i].num; j++)
			cout << "V" << path[i].pnode[j] << ", ";
		cout << "V" << path[i].pnode[path[i].num] << ")";
		cout << endl;
	}
}

//Floyd() - 多源最短路问题
template <class T>
bool MGraph<T>::Floyd()
{
	Vertex i, j, k;
	int D[MaxVertexNum][MaxVertexNum];

	//初始化
	for (i = 0; i < Nv; i++)
		for (j = 0; j < Nv; j++)
		{
			D[i][j] = G[i][j];
			p[i][j] = j;
		}

	//Floyd核心算法
	for (k = 0; k < Nv; k++)
		for (i = 0; i < Nv; i++)
			for (j = 0; j < Nv; j++)
				if (D[i][k] + D[k][j] < D[i][j])
				{
					D[i][j] = D[i][k] + D[k][j];
					if (i == j && D[i][j] < 0)  //若发现负值圈
						return false;   //不能正确解决,返回错误标记
					p[i][j] = k;
				}

	return true;
}

//PrintFloyd() - output多源最短路径问题的result
template <class T>
void MGraph<T>::PrintFloyd()
{
	cout << "各个顶点对的最短路径:" << endl;
	int row = 0;
	int col = 0;
	int temp = 0;
	for (row = 0; row < Nv; row++) {
		for (col = row + 1; col < Nv; col++) {
			cout << "v" << row << "---" << "v" << col << " p: " << " v" << row;
			temp = p[row][col];
			//循环输出途径的每条路径。
			while (temp != col) {
				cout << "-->" << "v" << temp;
				temp = p[temp][col];
			}
			cout << "-->" << "v" << col << endl;
		}

		cout << endl;
	}

}

//Prim() - 最小生成树算法
template <class T>
void MGraph<T>::Prim(MGraph& PrimTree)
{
	bool visited[MaxVertexNum];
	int i, j, k, h;
	int power, power_j, power_k;

	for (i = 0; i < Nv; i++)
		visited[i] = false;

	PrimTree.Nv = Nv;
	
	for (i = 0; i < Nv; i++)//初始化矩阵
	{
		for (j = 0; j < Nv; j++)
		{
			PrimTree.G[i][j] = MAXNUM;
		}
	}

	visited[0] = true;
	for (i = 0; i < Nv; i++)
	{
		power = MAXNUM;
		for (j = 0; j < Nv; j++)
		{
			if (visited[j])
			{
				for (k = 0; k < Nv; k++)
				{
					if (power > G[j][k] && !visited[k])
					{
						power = G[j][k];
						power_j = j;
						power_k = k;
					}
				}
			}
		}
		//min power
		if (!visited[power_k])
		{
			visited[power_k] = true;
			PrimTree.G[power_j][power_k] = power;
		}
	}
}

//DispPrim() - 输出最小生成树
template <class T>
void MGraph<T>::DispPrim()
{
	int i, j;

	for (i = 0; i < Nv; i++)
	{
		for (j = 0; j < Nv; j++)
		{
			cout << "\t" << G[i][j];
		}
		cout << endl;
	}
}

int main()
{
	MGraph<int> mGraph;
	MGraph<int> PrimTree;
	//建立图
	mGraph.BuildGraph();
	
	//求解单源最短路径 
	mGraph.Dijkstra(0);
	mGraph.DisDijPath();
	
	//求解多源最短路径 
	mGraph.Floyd();
	mGraph.PrintFloyd();
	
	//最小生成树
	mGraph.Prim(PrimTree);
	PrimTree.DispPrim();
	
	return 0; 
}

运行结果:

顶点数:5
边数:6
以起点、终点、权重的格式读入边
0 1 7
0 2 2
2 3 3
1 3 9
3 4 4
2 4 15
        从V0到各个顶点的最短路径长度如下
        (起点->终点)  最短长度    最短路径
        ------------  -------     --------
         (V0->V1):       7        (V0, V1)
         (V0->V2):       2        (V0, V2)
         (V0->V3):       5        (V0, V2, V3)
         (V0->V4):       9        (V0, V2, V3, V4)
各个顶点对的最短路径:
v0---v1 p:  v0-->v1
v0---v2 p:  v0-->v2
v0---v3 p:  v0-->v2-->v3
v0---v4 p:  v0-->v3-->v4
v1---v2 p:  v1-->v0-->v2
v1---v3 p:  v1-->v3
v1---v4 p:  v1-->v3-->v4
v2---v3 p:  v2-->v3
v2---v4 p:  v2-->v3-->v4
v3---v4 p:  v3-->v4

        65535   7           2           65535   65535
        65535   65535   65535   65535   65535
        65535   65535   65535   3           65535
        65535   65535   65535   65535   4
        65535   65535   65535   65535   65535
--------------------------------
Process exited after 56.5 seconds with return value 0
请按任意键继续. . .

猜你喜欢

转载自blog.csdn.net/y_16041527/article/details/80370054