【算法】单源最短路径之Dijkstra算法(未优化)实现

1. 邻接矩阵实现

需要注意的一点是在定义最短路径数组ShortestPathTable和邻接矩阵arcs及变量min_时要定义为long long int而不是int,因为在初始化邻接矩阵时我们将inf定义为int型数据最大值0x7fffffff,那么在D[j]的松弛操作中可能涉及某个值加inf,这样就会产生数值溢出,最后导致求出错误的最短路径D[j]

C++代码
/*单源最短路径——邻接矩阵实现*/
#define MAX_VERTEX_NUM 100
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;

const int INF = 0x7fffffff;
typedef int VertexType; //顶点类型 
typedef long long int ShortestPathTable[MAX_VERTEX_NUM]; //最短路径表 
typedef long long int  AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //邻接矩阵 
typedef int PathMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM]; //路径数组 


typedef struct {
	VertexType vexs[MAX_VERTEX_NUM]; //顶点表 
	AdjMatrix arcs; //邻接矩阵 
	int vexnum; //顶点数 
	int arcnum; //边数 
	int GraphKind;//图的种类 
}MGraph;//图 

/*定位顶点*/
int LocateVertex(MGraph& G, VertexType v)
{
	int i;
	for (i = 0; i < G.vexnum; i++)
		if (v == G.vexs[i])
			return i;
	return -1;
}

/*创建无向网*/
void CreateGraph(MGraph& G)
{
	cout << "请输入顶点数:";
	cin >> G.vexnum;
	cout << "请输入边数:";
	cin >> G.arcnum;

	//初始化顶点值 
	for (int i = 0; i < G.vexnum; i++)
		G.vexs[i] = i + 1;

	//初始化邻接矩阵都为无穷大 
	for (int i = 0; i < G.vexnum; i++)
		for (int j = 0; j < G.vexnum; j++)
			G.arcs[i][j] = INF;

	cout << "请输入与边关联的顶点和权值信息:" << endl;
	//输入顶点及权值信息 
	for (int k = 0; k < G.arcnum; k++)
	{
		VertexType v1, v2;
		int w;
		cin >> v1 >> v2 >> w;
		int i = LocateVertex(G, v1);
		int j = LocateVertex(G, v2);
		G.arcs[i][j] = w;
		//G.arcs[j][i] = w;
	}
}

/*Dijkstra算法求解最短路径
1. 初始化最短路径数组D[]为初始顶点v0到其余各点的距离
2. 将初始顶点加入集合S
3. 循环(顶点数 - 1)次 源点v0除外
	1. 求出D[]中最小值
	2. 将最小值对应的终点加入集合S
	3. 更新D[]数组,进行松弛操作(因为S集合变了 那么源点v0到集合V-S中顶点的距离可能变短)
*/
void ShortestPath_DIJ(MGraph& G, ShortestPathTable& D, PathMatrix& P, VertexType v0)
{
	int k = LocateVertex(G, v0);
	int final[MAX_VERTEX_NUM] = { 0 };//辅助数组 final[v]=1当且仅当v∈S,即已经求得v0到v的最短路径 
	final[k] = 1;// v0 加入集合S 
	for (int i = 0; i < G.vexnum; i++)
	{
		D[i] = G.arcs[k][i];
		for (int j = 0; j < G.vexnum; j++)//初始化路径数组 为空0  P[i][j]为1 则j是v0->i当前求得最短路径上的顶点 
		{
			P[i][j] = 0;
		}
		if (D[i] < INF) //v0和i之间连通 
		{
			P[i][k] = 1;//v0肯定是v0->i上的点 
			P[i][i] = 1;//i肯定是v0->i上的点	
		}
	}
	D[k] = 0; //v0 到 v0 距离为0

	for (int i = 1; i < G.vexnum; i++)//需要循环(顶点数-1)次即可 
	{
		int min_ = INF;
		int flag = 0;//标记最小值的下标 
		for (int j = 0; j < G.vexnum; j++) //1. 求出D[]中的最小值 
		{
			if (!final[j] && D[j] < min_)//要跳过v0顶点 
			{
				min_ = D[j];
				flag = j;
			}
		}
		final[flag] = 1;//2. 将顶点flag加入S
		//3. 对D[]进行松弛操作
		for (int j = 0; j < G.vexnum; j++)
		{
			//!final[j] 属于V-S  final[j]属于S 在S中寻找D[flag] + (flag,j) 使得 D[j] 更小 
			//if(!final[j] && D[flag] + G.arcs[flag][j] < D[j])
			if (!final[j] && min_ + G.arcs[flag][j] < D[j])
			{
				D[j] = min_ + G.arcs[flag][j];
				memcpy(P[j], P[flag], sizeof(P[j]));//将flag顶点的路径数组 赋给 j顶点的路径数组
				P[j][j] = 1;//j顶点在v0->j的当前最短路径上 
			}
		}
	}
}
int main()
{
	ShortestPathTable D;
	MGraph G;
	PathMatrix P;
	CreateGraph(G);
	cout << "请输入起始顶点:";
	VertexType v0;
	cin >> v0;
	ShortestPath_DIJ(G, D, P, v0);
	for (int i = 0; i < G.vexnum; i++)
	{ 
		cout << v0 << "-->" << G.vexs[i] << ":";
		if(D[i] == INF)
			cout<<"inf";
		else
			cout <<D[i];
		cout << "; 最短路径经过的顶点:";

		for (int j = 0; j < G.vexnum; j++)
		{
			if (P[i][j] == 1)
			{
				cout << G.vexs[j] << " ";
			}
		}
		cout << endl;
	}
	system("pause");
	return 0;
}

/*
4 6
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4

6 8
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
*/
输出结果

请输入顶点数:6
请输入边数:8
请输入与边关联的顶点和权值信息:
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
请输入起始顶点:2
2–>1:inf; 最短路径经过的顶点:
2–>2:0; 最短路径经过的顶点:
2–>3:5; 最短路径经过的顶点:2 3
2–>4:55; 最短路径经过的顶点:2 3 4
2–>5:inf; 最短路径经过的顶点:
2–>6:65; 最短路径经过的顶点:2 3 4 6

2. 邻接链表实现

此处和邻接矩阵实现方法一样,需要将info类型和ShortestPathTable设置为long long,同时代码中的变量min_也要设置为long long

C++代码
#include<iostream>
using namespace std;

typedef int VertexType;
#define MAX_VERTEX_NUM 100
#define INF  0x7fffffff 
typedef struct ArcNode
{
	int adjvex;//弧指向的顶点的位置
	ArcNode* nextarc;//指向下一个与该顶点邻接的顶点
	long long info;//弧的相关信息
}ArcNode;//边表结点

typedef struct VNode
{
	VertexType data;//用于存储顶点
	ArcNode* firstarc;//指向第一个与该顶点邻接的顶点
}VNode, AdjList[MAX_VERTEX_NUM];//表头节点,顺序表存储


typedef struct
{
	AdjList vertices;//邻接表
	int vexnum, arcnum;//边数,顶点数
	int kind;//图的种类
}ALGraph;

typedef long long int ShortPathTable[MAX_VERTEX_NUM]; 	 //最短路径长度

int LocateVertex(ALGraph& G, VertexType& v)
{
	int i;
	for (i = 0; i < G.vexnum; i++)
		if (G.vertices[i].data == v)
			return i;
	return -1;
}
void CreateGraph(ALGraph& G)
{

	cout << "请输入顶点数:";
	cin >> G.vexnum;
	cout << "请输入边数:";
	cin >> G.arcnum;

	for (int i = 0; i < G.vexnum; i++)
	{
		G.vertices[i].data = i + 1;
		G.vertices[i].firstarc = NULL;
	}

	cout << "请输入与边关联的顶点和权值信息:" << endl;
	for (int k = 0; k < G.arcnum; k++)//无向图
	{
		VertexType v1, v2;
		int w;
		cin >> v1 >> v2 >> w;
		int i = LocateVertex(G, v1);
		int j = LocateVertex(G, v2);
		ArcNode* p;
		/*j为入i为出创建邻接链表*/
		p = new ArcNode;
		p->adjvex = j;
		p->info = w;
		p->nextarc = G.vertices[i].firstarc;
		G.vertices[i].firstarc = p;
		/*i为入j为出创建邻接链表*/
//		p = new ArcNode;
//		p->adjvex = i;
//		p->info = w;
//		p->nextarc = G.vertices[j].firstarc;
//		G.vertices[j].firstarc = p;
	}
}
void ShortestPath_DIJ(ALGraph& G, ShortPathTable& D, VertexType v0)
{
	//用Dijkstra算法求有向网v0到其余各顶点带权长度D[v];
	//final[v]=true当且仅当v∈S,即已经求得v0到v的最短路径
	int final[MAX_VERTEX_NUM] = { 0 };

	for(int i = 0; i < G.vexnum; i++)
		D[i] = INF;
	
	int k = LocateVertex(G, v0);
	ArcNode* p = G.vertices[k].firstarc;
	while (p)
	{
		int w = p->adjvex;
		D[w] = p->info;
		p = p->nextarc;
	}

	D[k] = 0;
	final[k] = 1;//初始化,v0顶点属于S;
	for (int i = 1; i < G.vexnum; i++)
	{
		long long int min_ = INF;
		int v = 0;
		for (int j = 0; j < G.vexnum; j++)
		{
			if (!final[j] && D[j] < min_)
			{
				v = j;
				min_ = D[j];
			}
		}
		final[v] = 1;
		//更新当前最短距离D[];
		p = G.vertices[v].firstarc;
		while (p)
		{
			int w = p->adjvex;
			if (!final[w] && min_ + p->info < D[w])
				D[w] = min_ + p->info;
			p = p->nextarc;
		}
	}
}
int main()
{
	ALGraph G;
	ShortPathTable D;
	int v0;
	CreateGraph(G);
	cout << "请输入起始顶点:";
	cin >> v0;
	ShortestPath_DIJ(G, D, v0);
	for (int i = 0; i < G.vexnum; i++)
	{
		cout << v0 << "-->" << G.vertices[i].data << ":";
		if (D[i] == INF)
			cout << "inf";
		else
			cout << D[i];
		cout << endl;
	}
	cout << endl;
	return 0;
}
输出结果

请输入顶点数:6
请输入边数:8
请输入与边关联的顶点和权值信息:
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60
请输入起始顶点:2
2–>1:inf
2–>2:0
2–>3:5
2–>4:55
2–>5:inf
2–>6:65

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值