C++最短路径Dijkstra算法的分析与具体实现详解

 更新时间:2023年03月10日 09:13:11   作者:微凉秋意  
经典的求解最短路径算法有这么几种:广度优先算法、Dijkstra算法、Floyd算法。本文是对 Dijkstra算法的总结,该算法适用于带权有向图,可求出起始顶点到其他任意顶点的最小代价以及对应路径,希望对大家有所帮助

前言

经典的求解最短路径算法有这么几种:广度优先算法Dijkstra算法、Floyd算法,在此专栏中我都会将这些算法的分析与具体实现详细的展现出来。此篇文章是对 Dijkstra算法的总结,该算法适用于带权有向图,可求出起始顶点到其他任意顶点的最小代价以及对应路径。

Dijkstra 算法分析

一般来说,有关图的算法的存储结构为邻接表、邻接矩阵,这次就以邻接矩阵存储为例,求出下图的最短路径:

初始条件

需要有三个数组:

  • final[]:布尔型,用来记录顶点是否已找到最短路径
  • dist[]:整形,记录最短路径长度(带权)
  • path[]:整形,记录当前顶点的前驱结点下标
  #define MAXVERTEX 6
  bool final[MAXVERTEX];
  int dist[MAXVERTEX];
  int path[MAXVERTEX];

对于起始顶点需要将final 设为true,dist 设为 0,path 设为-1

第一轮

遍历所有与起始顶点相连的结点,找到一个权值最小的边,并将对应顶点 i 加入到最短路径,即 final[i] = true,之后再遍历与 i 相邻的顶点,若final 值为false 且dist 值小于dist[i]+dist[i][] 就将其dist 值更新,path 值改为 i。

第二轮及以后

第一轮结束后会有两个顶点的 final 值为 true,实际上最大的循环只需要进行n - 1次,从第一轮结束后我们从值为 false 的顶点中找 dist 值最小的顶点,将其fianl 值设为 true,检查与其相邻顶点的path 值和dist 值可否更新(判断与dist[i]+dist[i][]的大小),重复第二轮的操作直至大循环结束。这样最终的 dist 存放的就是起始顶点到对应下标顶点的最短路径长度,而path 存放的就是最短路径。

Dijkstra 代码实现

#include<iostream>
using namespace std;
// 模拟实现Dijkstra算法,不适用于存在负值的带权图
#define MAXVERTEX 6
typedef struct {
	char Vertex[MAXVERTEX]; //顶点集
	int Edge[MAXVERTEX][MAXVERTEX]; // 存放权值
	int vernum, arcnum; // 顶点数和边数
}MGraph;

// 初始化图
void InitGraph(MGraph& G) {
	G.Vertex[0] = 'A';
	G.Vertex[1] = 'B';
	G.Vertex[2] = 'C';
	G.Vertex[3] = 'D';
	G.Vertex[4] = 'E';
	G.vernum = 5;
	G.arcnum = 10;

	// 图中边权值均设为无穷大
	for (int i = 0; i < G.vernum; i++) {
		for (int j = 0; j < G.vernum; j++) {
			G.Edge[i][j] = INT_MAX;
		}
	}
	// 根据具体图形设置具体权值
	G.Edge[0][1] = 10; // 诸如此类
	G.Edge[0][4] = 5;
	G.Edge[1][2] = 1;
	G.Edge[1][4] = 2;
	G.Edge[4][1] = 3;
	G.Edge[2][3] = 4;
	G.Edge[3][2] = 6;
	G.Edge[4][3] = 2;
	G.Edge[3][0] = 7;
	G.Edge[4][2] = 9;
}
bool final[MAXVERTEX];
int dist[MAXVERTEX];
int path[MAXVERTEX];

void Dijkstra(MGraph G,int v) {
	for (int i = 0; i < G.vernum; i++) {
		final[i] = false;
		dist[i] = G.Edge[v][i];
		path[i] = (G.Edge[v][i] == INT_MAX ? -1 : v);
	}
	final[v] = true;
	dist[v] = 0;
	// 第一轮
	int index =v; // 权值最小的边顶点下标
	int para = INT_MAX;
	for (int j = 0; j < G.vernum; j++) {
		if (final[j] == false && G.Edge[v][j] < para) {
			para = G.Edge[v][j];
			index = j;
		}
	}
	// 第二轮及以后
	for (int i = 0; i < G.vernum; i++) {
		for (int c = 0; c < G.vernum; c++) {
			if (final[c] ==false && G.Edge[index][c] < INT_MAX) {
				if (G.Edge[index][c] + dist[index] < dist[c]) {
					dist[c] = G.Edge[index][c] + dist[index];
					path[c] = index;
				}
			}
		}
		// 找到final 为false的顶点中权值最小的顶点下标
		int temp = INT_MAX;
		int in = v;
		for (int i = 0; i < G.vernum; i++) {
			if (final[i] == false && dist[i] < temp) {
				temp = dist[i];
				in = i;
			}
		}
		index = in; // 更新下标
		final[index] = true;
	}
}

void print_path(MGraph G ,int v) {
	cout << "对应的最短路径为:";
	cout << G.Vertex[v] << "->";
	for (int i = 0; i < G.vernum; i++) {
		if (path[v] != 1) {
			cout << G.Vertex[path[v]] << "->";
			v = path[v];
		}
	}
	cout << G.Vertex[1] << endl;
}

int main() {
	MGraph G;
	InitGraph(G);
	Dijkstra(G, 1);
	cout << "顶点B到顶点D的最小花费为:"<< dist[3] << endl;
	print_path(G, 3);
}

运行结果:

输入输出格式

想得到哪个顶点的最短路径就在主函数中 Dijkstra(G, ?) 第二个参数写入下标即可,其他对应关系:顶点下标 0~4 对应 A~E,所以在 cout那行代码中dist下标要与到达顶点一致,而出发顶点要与自己填入的下标一致。

print_path 函数里的 if 语句中的下标也要和起始顶点下标一致,最后的一个cout也同样处理

例如:

Dijkstra(G,0);
// dist[2];
cout<<"顶点A到顶点C的最短路径为"<<dist[2]<<endl;
void print_path(MGraph G ,int v) {
	cout << "对应的最短路径为:";
	cout << G.Vertex[v] << "->";
	for (int i = 0; i < G.vernum; i++) {
		if (path[v] != 0) {
			cout << G.Vertex[path[v]] << "->";
			v = path[v];
		}
	}
	cout << G.Vertex[0] << endl;
}

时间复杂度

Dijkstra 算法的时间复杂度只与顶点有关,可以通过算法分析看出来每次都要对一个顶点遍历寻找与其相邻顶点的最小权值,所以时间复杂度应为:O(n2),也可以写成O(∣V∣2),V 是顶点的含义(vertex)。

到此这篇关于C++最短路径Dijkstra算法的分析与具体实现详解的文章就介绍到这了,更多相关C++最短路径Dijkstra算法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++代码实现网络Ping功能

    C++代码实现网络Ping功能

    这篇文章主要介绍了C++代码实现网络Ping功能,Ping命令被送到本地计算机的IP软件,该命令永不退出该计算机,本文给大家介绍的非常详细,需要的朋友参考下吧
    2021-08-08
  • C语言数据存储详解

    C语言数据存储详解

    在本篇文章里小编给大家整理的是关C语言数据存储,小编觉得这篇文章写的很不错,有需要的朋友们可以学习参考下,希望能够给你带来帮助
    2021-10-10
  • C语言单链表的实现

    C语言单链表的实现

    单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。这篇文章主要介绍了C语言单链表的实现 的相关资料,需要的朋友可以参考下
    2016-04-04
  • C++指针与引用的异同

    C++指针与引用的异同

    这篇文章主要介绍了C++指针与引用的异同,文章以C++指针与引用的相关资料结合指针和引用的相同点和区别展开详细内容,需要的朋友可以参考一下
    2021-11-11
  • 结构体类型数据作为函数参数(三种方法)

    结构体类型数据作为函数参数(三种方法)

    将一个结构体中变量中的数据传递给另一个函数,有以下三种方法。需要的朋友可以过来参考下,希望对大家有所帮助
    2013-10-10
  • C 语言简单加减乘除运算

    C 语言简单加减乘除运算

    本篇文章主要介绍了C语言的基本运算方法,这里对加法,减法,乘法,除法,求余数,做了详细讲解,有需要的朋友可以参考下
    2016-07-07
  • C语言中初始、增加和删除进程信号的操作方法简介

    C语言中初始、增加和删除进程信号的操作方法简介

    这篇文章主要介绍了C语言中初始、增加和删除进程信号的操作方法简介,分别是sigemptyset函数、sigaddset函数和sigdelset函数的用法,需要的朋友可以参考下
    2015-09-09
  • C++实现LeetCode(141.单链表中的环)

    C++实现LeetCode(141.单链表中的环)

    这篇文章主要介绍了C++实现LeetCode(141.单链表中的环),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++发送HTTP请求的实现代码

    C++发送HTTP请求的实现代码

    这篇文章主要介绍了C++发送HTTP请求的实现代码,需要的朋友可以参考下
    2014-06-06
  • Qt模仿实现文字浮动字母的效果

    Qt模仿实现文字浮动字母的效果

    这篇文章主要介绍了通过Qt实现的文字浮动的效果,效果很简单就是文本向上移动,在移动过程中文字整体变大或缩小。感兴趣的可以试一试
    2022-01-01

最新评论