栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

Dijkstra算法实现求有向图中一顶点到其余各个顶点的最短路径

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Dijkstra算法实现求有向图中一顶点到其余各个顶点的最短路径

一、文章说明:
  1. C++语言实现;
  2. 有向图的存储结构为: 邻接矩阵;
  3. 这篇文章的代码是我根据B站UP主懒猫老师所写的,能成功运行,VS里面显示有很多警告。而且还可能存在有未调试出的BUG,请多多指教。
  4. 观看懒猫老师的视频后,才方便理解博主代码,不然可能理解起来会很吃力。
二、 算法思想与实现思路:

请前往B站观看 up主 懒猫老师 的教学视频;
——附:老师思路清楚,并且通过形象的PPT动画来模拟算法实现过程,非常有利于理解整个算法过程!

视频链接:
1.算法思想:
懒猫老师-数据结构-(46)最短路径(Dijkstra算法,迪杰斯特拉算法,单源最短路径)

2.算法实现过程:
懒猫老师-数据结构-(47)最短路径(Dijkstra算法实现,迪杰斯特拉算法,单源最短路径)

三、3个核心函数:

1.迪杰斯特拉算法函数;

void Dijkstra(AMGraph g)
{
	int start;
	char c;
	cout << "请输入最小路径的源点:" << "t";
	cin >> c;                                                
	start = g.Locate_vex(c);                                        //利用定位函数查找源点在顶点表当中的序号;

	//1.用动态分配的方式,为路径path, 路径权值dist, 算法思想中的S数组开辟空间。
	//注意这里path,dist,S数组单元的序号所对应的顶点和类 AMGraph中顶点表 vexs 所对应的顶点是一样的!

	int* path = new int[g.vexnum];                                  //空间大小为 图的顶点个数,vexnum;
	int* dist = new int[g.vexnum];
	int* S = new int[g.vexnum];


	//2. 初始化这三个数组;
	for (int i = 0; i < g.vexnum; i++)
	{
		dist[i] = g.arcs[start][i];
		if (dist[i] != Maxlnt) path[i] = start;
		else path[i] = -1;
	}

	for (int i = 0; i < g.vexnum; i++)
	{
		S[i] = 0;                                                    // 0代表未进入算法思想的S集合,1代表已经进入。
	}
	S[start] = 1;                                                    // 将源点加入S集合。
	int num = 1;                                                     //计数变量,循环控制条件;
	int min;

    //3. 正式进入算法,求源点到其它顶点的最短路径。
	while (num < g.vexnum)
	{
		min = findMinDist(dist, S, g.vexnum);                        //查找在当前顶点所邻接后继结点的边中,具有最小权值的后继结点的序号。
		S[min] = 1;
		for (int i = 0; i < g.vexnum; i++)                           //对路径数组path 和路径权值数组dist 进行更新。
		{
			if ((S[i] == 0) && (dist[i] > dist[min] + g.arcs[min][i]))
			{
				dist[i] = dist[min] + g.arcs[min][i];
				path[i] = min;
			}
		}
		num++;
	}
	displayPath(dist, path, start, g);
	delete[]path;
	delete[]dist;
	delete[]S;
}

2.findMinDist()函数;
函数功能:
查找当前结点与后继邻接顶点中具有最小权值的边,的邻接顶点的序号

int findMinDist(int dist[], int S[], int n)
{
	int MIN = Maxlnt + 1;
	int mark = -1;
	for (int i = 0; i < n; i++)
	{
		if (S[i] == 0)
		{
			if (dist[i] < MIN)
			{
				MIN = dist[i];
				mark = i;
			}
		}
	}
	return mark;
}

3. 路径输出函数displayPath();
函数功能:

  • 一、如果对于除了源点的其它顶点,如果源点是可达该顶点的,则输出其的最小路径和权值
  • 二、输出源点所到达不了的顶点,说明这源点和这个顶点之间不存在有最短路径。
void displayPath(int dist[], int path[], int start, AMGraph g)
{
	char* road = new char[g.vexnum];
	int number;                                      //number 用来表示源点到第i号顶点路径的长度;
	for (int i = 0; i < g.vexnum; i++)
	{
		int j = i;                                   //这里j可以从充当一个指针,一开始指向每条路径的终点。
		if (j != start && dist[j] != Maxlnt)         //注意这里j指向的终点必须是可达源点的,所以要加上判断条件 dist[j] 不可以等于无穷大 Maxlnt ;
		{
			number = -1;

			//1.利用while循环,从最短路径的终点向源点逐步衍生,逐步扩大road数组
			while (g.vexs[j] != g.vexs[start])        //当j 指向源点的时候,循环结束。
			{
				number++;
				road[number] = g.vexs[j];
				j = path[j];                          //j指向当前顶点的,前驱邻接顶点。
				if (j == start)                       //如果 j 指向源点了,将源点加入road 数组,数组中存储的就是逆序的路径了,number为路径的长度。
				{
					number++;
					road[number] = g.vexs[j];
				}
			}

			//2.利用for循环,将路径数组逆序输出即可得到 源点到第 i 号顶点的最小路径。
			cout << endl << g.vexs[start] << " to " << g.vexs[i] << " " << "路径权值:" << dist[i] << "t" << "路径为: ";
			for (int k = number; k >= 0; k--)
			{
				if (k == 0)
				{
					cout << road[k] << endl;
				}
				else cout << road[k] << "—>";
			}
			cout << endl;
		}
	}

	//3.布尔变量adjust, 用来判断源点是否有到达不了的顶点, 如果有则利用for循环输出。
	bool adjust = true;
	for (int i = 0; i < g.vexnum; i++)
	{
		if (dist[i] == Maxlnt)
		{
			adjust = false;
			break;
		}
	}
	if (adjust == false)
	{
		cout << endl << "源点:" << g.vexs[start] << "到达不了的顶点为:" << "t";
		for (int i = 0; i < g.vexnum; i++)
		{
			if (dist[i] == Maxlnt)
				cout << g.vexs[i] << "t";
		}
		cout << endl << endl;
	}
	delete[]road;
}
四、完整代码:

说明: 为了更方便地调试程序,我将算法具体实现写成了一个小小系统的形式,
所以代码会比较冗长,注释也比较多,阅读起来可能很不舒适。

#include
#include
using namespace std;

#define Maxlnt 32767
#define MVNum 20
#define OK 1

class AMGraph
{
private:
	char vexs[MVNum];                              //顶点表;
	int arcs[MVNum][MVNum];                        //边表;
	int vexnum, arcnum;                            //点的数量和边的数量;
public:
	bool creat_graph();
	int Locate_vex(char c);                        //顶点定位函数,定位顶点在顶点表当中的数字;
	void print();                                  // 图的信息打印函数;

	friend void displayPath(int dist[], int path[], int start, AMGraph g); // 路径输出函数;
	friend void Dijkstra(AMGraph g);


};

int AMGraph::Locate_vex(char c)
{
	for (int i = 0; i < vexnum; i++)
	{
		if (c == vexs[i]) return i;
	}
	return -1;
}

bool AMGraph::creat_graph()
{
	cout << "请依次输入图的顶点数目和边数:" << "t";
	cin >> vexnum >> arcnum;
	cout << endl << "请依次输入" << vexnum << "个顶点:" << "t";
	for (int i = 0; i < vexnum; i++)
	{
		cin >> vexs[i];
	}
	for (int i = 0; i < vexnum; i++)
	{
		for (int j = 0; j < vexnum; j++)
		{
			if (i == j) arcs[i][j] = 0;
			else arcs[i][j] = Maxlnt;
		}
	}
	cout << endl;
	for (int k = 0; k < arcnum; k++)
	{
		char v1, v2;
		int w;
		cout << "请分别输入第" << k + 1 << "条有向边的起点,终点和权值:" << "t";
		cin >> v1 >> v2 >> w;
		int i = Locate_vex(v1);
		int j = Locate_vex(v2);
		arcs[i][j] = w;
	}
	return OK;
}

void AMGraph::print()
{
	cout << endl << "顶点表的信息:" << endl;
	for (int i = 0; i < vexnum; i++)
	{
		cout << vexs[i] << " ";
	}
	cout << endl << endl;
	cout << "邻接矩阵的信息为:" << endl;

	// 这里for循环 从-1 开始打印邻接矩阵表格;
	for (int i = -1; i < vexnum; i++)
	{
		for (int j = -1; j < vexnum; j++)
		{
			//i=-1的时候, 需要输出边表中,  以i为起点的边所对应的终点;
			if (i == -1)
			{
				if (j == -1)cout << " " << "t";
				else cout << vexs[j] << "t";
				if (j == vexnum - 1) //换行条件控制
					cout << endl;
			}

			else
			{

				//j=-1的时候,需要输出边表中,  以j为终点的边所对应的起点: 
				if (j == -1)cout << vexs[i] << "t";
				else
				{
					if (arcs[i][j] == Maxlnt)
						cout << "∞" << "t";
					else cout << arcs[i][j] << "t";
					if (j == vexnum - 1) //换行条件控制
						cout << endl;
				}
			}
		}
	}
}

int findMinDist(int dist[], int S[], int n)
{
	int MIN = Maxlnt + 1;
	int mark = -1;
	for (int i = 0; i < n; i++)
	{
		if (S[i] == 0)
		{
			if (dist[i] < MIN)
			{
				MIN = dist[i];
				mark = i;
			}
		}
	}
	return mark;
}

void displayPath(int dist[], int path[], int start, AMGraph g)
{
	char* road = new char[g.vexnum];
	int number;                                      //number 用来表示源点到第i号顶点路径的长度;
	for (int i = 0; i < g.vexnum; i++)
	{
		int j = i;                                   //这里j可以从充当一个指针,一开始指向每条路径的终点。
		if (j != start && dist[j] != Maxlnt)         //注意这里j指向的终点必须是可达源点的,所以要加上判断条件 dist[j] 不可以等于无穷大 Maxlnt ;
		{
			number = -1;

			//1.利用while循环,从最短路径的终点向源点逐步衍生,逐步扩大road数组
			while (g.vexs[j] != g.vexs[start])        //当j 指向源点的时候,循环结束。
			{
				number++;
				road[number] = g.vexs[j];
				j = path[j];                          //j指向当前顶点的,前驱邻接顶点。
				if (j == start)                       //如果 j 指向源点了,将源点加入road 数组,数组中存储的就是逆序的路径了,number为路径的长度。
				{
					number++;
					road[number] = g.vexs[j];
				}
			}

			//2.利用for循环,将路径数组逆序输出即可得到 源点到第 i 号顶点的最小路径。
			cout << endl << g.vexs[start] << " to " << g.vexs[i] << " " << "路径权值:" << dist[i] << "t" << "路径为: ";
			for (int k = number; k >= 0; k--)
			{
				if (k == 0)
				{
					cout << road[k] << endl;
				}
				else cout << road[k] << "—>";
			}
			cout << endl;
		}
	}

	//3.布尔变量adjust, 用来判断源点是否有到达不了的顶点, 如果有则利用for循环输出。
	bool adjust = true;
	for (int i = 0; i < g.vexnum; i++)
	{
		if (dist[i] == Maxlnt)
		{
			adjust = false;
			break;
		}
	}
	if (adjust == false)
	{
		cout << endl << "源点:" << g.vexs[start] << "到达不了的顶点为:" << "t";
		for (int i = 0; i < g.vexnum; i++)
		{
			if (dist[i] == Maxlnt)
				cout << g.vexs[i] << "t";
		}
		cout << endl << endl;
	}
	delete[]road;
}

void Dijkstra(AMGraph g)
{
	int start;
	char c;
	cout << "请输入最小路径的源点:" << "t";
	cin >> c;                                                
	start = g.Locate_vex(c);                                        //利用定位函数查找源点在顶点表当中的序号;

	//1.用动态分配的方式,为路径path, 路径权值dist, 算法思想中的S数组开辟空间。
	//注意这里path,dist,S数组单元的序号所对应的顶点和类 AMGraph中顶点表 vexs 所对应的顶点是一样的!

	int* path = new int[g.vexnum];                                  //空间大小为 图的顶点个数,vexnum;
	int* dist = new int[g.vexnum];
	int* S = new int[g.vexnum];


	//2. 初始化这三个数组;
	for (int i = 0; i < g.vexnum; i++)
	{
		dist[i] = g.arcs[start][i];
		if (dist[i] != Maxlnt) path[i] = start;
		else path[i] = -1;
	}

	for (int i = 0; i < g.vexnum; i++)
	{
		S[i] = 0;                                                    // 0代表未进入算法思想的S集合,1代表已经进入。
	}
	S[start] = 1;                                                    // 将源点加入S集合。
	int num = 1;                                                     //计数变量,循环控制条件;
	int min;

    //3. 正式进入算法,求源点到其它顶点的最短路径。
	while (num < g.vexnum)
	{
		min = findMinDist(dist, S, g.vexnum);                        //查找在当前顶点所邻接后继结点的边中,具有最小权值的后继结点的序号。
		S[min] = 1;
		for (int i = 0; i < g.vexnum; i++)                           //对路径数组path 和路径权值数组dist 进行更新。
		{
			if ((S[i] == 0) && (dist[i] > dist[min] + g.arcs[min][i]))
			{
				dist[i] = dist[min] + g.arcs[min][i];
				path[i] = min;
			}
		}
		num++;
	}
	displayPath(dist, path, start, g);
	delete[]path;
	delete[]dist;
	delete[]S;
}

void menu()
{
	cout << "****《迪杰斯特拉算法求最短路径》*****" << endl;
	cout << "**1. 图的建立" << endl;
	cout << "**2. 输出图的信息" << endl;
	cout << "**3. 求图的最短路径" << endl;
	cout << "**4. 退出" << endl;
	cout << "____________________________________________" << endl;
}

void deletescreen()
{
	system("pause");
	system("cls");
}

int main()
{
	void menu();
	void deletescreen();
	AMGraph g1;
	//menu();
	string choose;
	while (true)
	{
		menu();
		cin >> choose;
		if (choose == "1")
		{
			g1.creat_graph();
			cout << endl << "图建立成功!" << endl;
			deletescreen();
		}
		else if (choose == "2")
		{
			g1.print();
			deletescreen();
		}
		else if (choose == "3")
		{
			Dijkstra(g1);
			deletescreen();
		}
		else if (choose == "4")
		{
			cout << "退出成功!" << endl;
			exit(0);
		}
		else
		{
			cout << "输入有误!请重新输入" << endl;
			deletescreen();
		}
	}
	return 0;
}
五、测试情况:

测试一:

求图片中
1.源点为A到其它顶点的最小路径
2.源点为D到其它顶点的最小路径

测试结果:
1.建图

2.图的信息:

3.A到其它顶点的最小路径

4.D到其它顶点的最小路径

测试二:
1.测试案例:

2.求以 0 作为源点,到其它顶点的最短路径:

感谢你的收看,希望这可以帮助到你。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/690901.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号