最短路径问题---Dijkstra算法详解
侵删https://blog.csdn.net/qq_35644234/article/details/60870719
前言
Nobody can go back and start a new beginning,but anyone can start today and make a new ending.
Name:Willam
Time:2017/3/8
1、最短路径问题介绍
问题解释:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径
解决问题的算法:
- 迪杰斯特拉算法(Dijkstra算法)
- 弗洛伊德算法(Floyd算法)
- SPFA算法
这篇博客,我们就对Dijkstra算法来做一个详细的介绍
2、Dijkstra算法介绍
算法特点:
迪科斯彻算法使用了广度优先搜索解决赋权有向图或者无向图的单源最短路径问题,算法最终得到一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。
算法的思路
Dijkstra算法采用的是一种贪心的策略,声明一个数组dis来保存源点到各个顶点的最短距离和一个保存已经找到了最短路径的顶点的集合:T,初始时,原点 s 的路径权重被赋为 0 (dis[s] = 0)。若对于顶点 s 存在能直接到达的边(s,m),则把dis[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大。初始时,集合T只有顶点s。
然后,从dis数组选择最小值,则该值就是源点s到该值对应的顶点的最短路径,并且把该点加入到T中,OK,此时完成一个顶点,
然后,我们需要看看新加入的顶点是否可以到达其他顶点并且看看通过该顶点到达其他点的路径长度是否比源点直接到达短,如果是,那么就替换这些顶点在dis中的值。
然后,又从dis中找出最小值,重复上述动作,直到T中包含了图的所有顶点。
3、Dijkstra算法示例演示
下面我求下图,从顶点v1到其他各个顶点的最短路径
首先第一步,我们先声明一个dis数组,该数组初始化的值为:
我们的顶点集T的初始化为:T={v1}
既然是求 v1顶点到其余各个顶点的最短路程,那就先找一个离 1 号顶点最近的顶点。通过数组 dis 可知当前离v1顶点最近是 v3顶点。当选择了 2 号顶点后,dis[2](下标从0开始)的值就已经从“估计值”变为了“确定值”,即 v1顶点到 v3顶点的最短路程就是当前 dis[2]值。将V3加入到T中。
为什么呢?因为目前离 v1顶点最近的是 v3顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 v1顶点到 v3顶点的路程进一步缩短了。因为 v1顶点到其它顶点的路程肯定没有 v1到 v3顶点短.
OK,既然确定了一个顶点的最短路径,下面我们就要根据这个新入的顶点V3会有出度,发现以v3 为弧尾的有: < v3,v4 >,那么我们看看路径:v1–v3–v4的长度是否比v1–v4短,其实这个已经是很明显的了,因为dis[3]代表的就是v1–v4的长度为无穷大,而v1–v3–v4的长度为:10+50=60,所以更新dis[3]的值,得到如下结果:
因此 dis[3]要更新为 60。这个过程有个专业术语叫做“松弛”。即 v1顶点到 v4顶点的路程即 dis[3],通过 < v3,v4> 这条边松弛成功。这便是 Dijkstra 算法的主要思想:通过“边”来松弛v1顶点到其余各个顶点的路程。
然后,我们又从除dis[2]和dis[0]外的其他值中寻找最小值,发现dis[4]的值最小,通过之前是解释的原理,可以知道v1到v5的最短距离就是dis[4]的值,然后,我们把v5加入到集合T中,然后,考虑v5的出度是否会影响我们的数组dis的值,v5有两条出度:< v5,v4>和 < v5,v6>,然后我们发现:v1–v5–v4的长度为:50,而dis[3]的值为60,所以我们要更新dis[3]的值.另外,v1-v5-v6的长度为:90,而dis[5]为100,所以我们需要更新dis[5]的值。更新后的dis数组如下图:
然后,继续从dis中选择未确定的顶点的值中选择一个最小的值,发现dis[3]的值是最小的,所以把v4加入到集合T中,此时集合T={v1,v3,v5,v4},然后,考虑v4的出度是否会影响我们的数组dis的值,v4有一条出度:< v4,v6>,然后我们发现:v1–v5–v4–v6的长度为:60,而dis[5]的值为90,所以我们要更新dis[5]的值,更新后的dis数组如下图:
然后,我们使用同样原理,分别确定了v6和v2的最短路径,最后dis的数组的值如下:
因此,从图中,我们可以发现v1-v2的值为:∞,代表没有路径从v1到达v2。所以我们得到的最后的结果为:
起点 终点 最短路径 长度
v1 v2 无 ∞
v3 {v1,v3} 10
v4 {v1,v5,v4} 50
v5 {v1,v5} 30
v6 {v1,v5,v4,v6} 60
- 1
- 2
- 3
- 4
- 5
- 6
- 7
4、Dijkstra算法的代码实现(c++)
- Dijkstra.h文件的代码
/************************************************************/
/* 程序作者:Willam */
/* 程序完成时间:2017/3/8 */
/* 有任何问题请联系:2930526477@qq.com */
/************************************************************/
//@尽量写出完美的程序
#pragma once
//#pragma once是一个比较常用的C/C++杂注,
//只要在头文件的最开始加入这条杂注,
//就能够保证头文件只被编译一次。
#include<iostream>
include<string>
using namespace std;
/*
本程序是使用Dijkstra算法实现求解最短路径的问题
采用的邻接矩阵来存储图
*/
//记录起点到每个顶点的最短路径的信息
struct Dis {
string path;
int value;
bool visit;
Dis() {
visit = false;
value = 0;
path = “”;
}
};
class Graph_DG {
private:
int vexnum; //图的顶点个数
int edge; //图的边数
int **arc; //邻接矩阵
Dis * dis; //记录各个顶点最短路径的信息
public:
//构造函数
Graph_DG(int vexnum, int edge);
//析构函数
~Graph_DG();
// 判断我们每次输入的的边的信息是否合法
//顶点从1开始编号
bool check_edge_value(int start, int end, int weight);
//创建图
void createGraph();
//打印邻接矩阵
void print();
//求最短路径
void Dijkstra(int begin);
//打印最短路径
void print_path(int);
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- Dijkstra.cpp文件的代码
#include"Dijkstra.h"
//构造函数
Graph_DG::Graph_DG(int vexnum, int edge) {
//初始化顶点数和边数
this->vexnum = vexnum;
this->edge = edge;
//为邻接矩阵开辟空间和赋初值
arc = new int*[this->vexnum];
dis = new Dis[this->vexnum];
for (int i = 0; i < this->vexnum; i++) {
arc[i] = new int[this->vexnum];
for (int k = 0; k < this->vexnum; k++) {
//邻接矩阵初始化为无穷大
arc[i][k] = INT_MAX;
}
}
}
//析构函数
Graph_DG::~Graph_DG() {
delete[] dis;
for (int i = 0; i < this->vexnum; i++) {
delete this->arc[i];
}
delete arc;
}
// 判断我们每次输入的的边的信息是否合法
//顶点从1开始编号
bool Graph_DG::check_edge_value(int start, int end, int weight) {
if (start<1 || end<1 || start>vexnum || end>vexnum || weight < 0) {
return false;
}
return true;
}
void Graph_DG::createGraph() {
cout << "请输入每条边的起点和终点(顶点编号从1开始)以及其权重" << endl;
int start;
int end;
int weight;
int count = 0;
while (count != this->edge) {
cin >> start >> end >> weight;
//首先判断边的信息是否合法
while (!this->check_edge_value(start, end, weight)) {
cout << "输入的边的信息不合法,请重新输入" << endl;
cin >> start >> end >> weight;
}
//对邻接矩阵对应上的点赋值
arc[start - 1][end - 1] = weight;
//无向图添加上这行代码
//arc[end - 1][start - 1] = weight;
++count;
}
}
void Graph_DG::print() {
cout << "图的邻接矩阵为:" << endl;
int count_row = 0; //打印行的标签
int count_col = 0; //打印列的标签
//开始打印
while (count_row != this->vexnum) {
count_col = 0;
while (count_col != this->vexnum) {
if (arc[count_row][count_col] == INT_MAX)
cout << "∞" << " ";
else
cout << arc[count_row][count_col] << " ";
++count_col;
}
cout << endl;
++count_row;
}
}
void Graph_DG::Dijkstra(int begin){
//首先初始化我们的dis数组
int i;
for (i = 0; i < this->vexnum; i++) {
//设置当前的路径
dis[i].path = "v" + to_string(begin) + "-->v" + to_string(i + 1);
dis[i].value = arc[begin - 1][i];
}
//设置起点的到起点的路径为0
dis[begin - 1].value = 0;
dis[begin - 1].visit = true;
int count = 1;
//计算剩余的顶点的最短路径(剩余this->vexnum-1个顶点)
while (count != this->vexnum) {
//temp用于保存当前dis数组中最小的那个下标
//min记录的当前的最小值
int temp=0;
int min = INT_MAX;
for (i = 0; i < this->vexnum; i++) {
if (!dis[i].visit && dis[i].value<min) {
min = dis[i].value;
temp = i;
}
}
//cout << temp + 1 << " "<<min << endl;
//把temp对应的顶点加入到已经找到的最短路径的集合中
dis[temp].visit = true;
++count;
for (i = 0; i < this->vexnum; i++) {
//注意这里的条件arc[temp][i]!=INT_MAX必须加,不然会出现溢出,从而造成程序异常
if (!dis[i].visit && arc[temp][i]!=INT_MAX && (dis[temp].value + arc[temp][i]) < dis[i].value) {
//如果新得到的边可以影响其他为访问的顶点,那就就更新它的最短路径和长度
dis[i].value = dis[temp].value + arc[temp][i];
dis[i].path = dis[temp].path + "-->v" + to_string(i + 1);
}
}
}
}
void Graph_DG::print_path(int begin) {
string str;
str = "v" + to_string(begin);
cout << "以"<<str<<"为起点的图的最短路径为:" << endl;
for (int i = 0; i != this->vexnum; i++) {
if(dis[i].value!=INT_MAX)
cout << dis[i].path << "=" << dis[i].value << endl;
else {
cout << dis[i].path << "是无最短路径的" << endl;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- main.cpp文件的代码
#include"Dijkstra.h"
//检验输入边数和顶点数的值是否有效,可以自己推算为啥:
//顶点数和边数的关系是:((Vexnum*(Vexnum - 1)) / 2) < edge
bool check(int Vexnum, int edge) {
if (Vexnum <= 0 || edge <= 0 || ((Vexnum*(Vexnum - 1)) / 2) < edge)
return false;
return true;
}
int main() {
int vexnum; int edge;
cout << "输入图的顶点个数和边的条数:" << endl;
cin >> vexnum >> edge;
while (!check(vexnum, edge)) {
cout << "输入的数值不合法,请重新输入" << endl;
cin >> vexnum >> edge;
}
Graph_DG graph(vexnum, edge);
graph.createGraph();
graph.print();
graph.Dijkstra(1);
graph.print_path(1);
system("pause");
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
输入:
6 8
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 6 60
5 4 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
输出:
从输出可以看出,程序的结果和我们之前手动计算的结果是一样的。
最短路径问题---Dijkstra算法详解的更多相关文章
- 图的最短路径-----------Dijkstra算法详解(TjuOj2870_The Kth City)
做OJ需要用到搜索最短路径的题,于是整理了一下关于图的搜索算法: 图的搜索大致有三种比较常用的算法: 迪杰斯特拉算法(Dijkstra算法) 弗洛伊德算法(Floyd算法) SPFA算法 Dijkst ...
- Dijkstra算法详解
1.dijkstra算法简介 Dijkstra算法是由E.W.Dijkstra于1959年提出,又叫迪杰斯特拉算法,它应用了贪心算法模式,是目前公认的最好的求解最短路径的方法.算法解决的是有向图中单个 ...
- HDU 1874 畅通工程续-- Dijkstra算法详解 单源点最短路问题
参考 此题Dijkstra算法,一次AC.这个算法时间复杂度O(n2)附上该算法的演示图(来自维基百科): 附上: 迪科斯彻算法分解(优酷) problem link -> HDU 1874 ...
- 【最短路径Floyd算法详解推导过程】看完这篇,你还能不懂Floyd算法?还不会?
简介 Floyd-Warshall算法(Floyd-Warshall algorithm),是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似.该算法名称以 ...
- HDU1548——A strange lift(最短路径:dijkstra算法)
A strange lift DescriptionThere is a strange lift.The lift can stop can at every floor as you want, ...
- BM算法 Boyer-Moore高质量实现代码详解与算法详解
Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...
- kmp算法详解
转自:http://blog.csdn.net/ddupd/article/details/19899263 KMP算法详解 KMP算法简介: KMP算法是一种高效的字符串匹配算法,关于字符串匹配最简 ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- [转] KMP算法详解
转载自:http://www.matrix67.com/blog/archives/115 KMP算法详解 如果机房马上要关门了,或者你急着要和MM约会,请直接跳到第六个自然段. 我们这里说的K ...
随机推荐
- Linux 查看系统硬件信息[转]
原文:http://www.cnblogs.com/ggjucheng/archive/2013/01/14/2859613.html linux查看系统的硬件信息,并不像windows那么直观,这里 ...
- Scala 继承
1. 继承 Scala 通过 extends 关键字来继承类. 那么继承一个类有什么好处呢? 子类拥有继承自超类的方法和字段(即为val(常量), var(变量)所定义的) 可以添加自己需要的新方法和 ...
- 第六章 MVC之 FileResult和JS请求二进制流文件
一.FileResult 1.简介 表示一个用于将二进制文件内容发送到响应的基类.它有三个子类: FileContentResultFilePathResultFileStreamResult 推荐阅 ...
- <杂记>该换个背景图了
..当然我刚开始也是懵逼的,我有发现这里可以写css,但是还是缺个图片地址,想了想,这不是还有个相册功能吗. 那应该就是把自己要换的图片上传到相册吧. 右击图片,选择检查元素找到图片的src 如:ht ...
- 根据CAS协议写的简单的SSO框架
前言: 考虑到现在分布式应用都不可或缺的一个重要部分:单点登录,决定花点时间去学下.本来想直接上现成的CAS框架的,初步的了解了一下后,觉得这个太庞大了,而且不好定制,要完全深度用起来也没那么简单 ...
- PHP中empty,isset,is_null的区别
isset 判断变量是否已存在 empty 判断变量是否为空或为0 is_null 判断变量是否为NULL 仅作为记录使用. 参考链接:http://www.jb51.net/article/6903 ...
- Python-爬虫-租房Ziroom
目标站点需求分析 涉及的库 import requestsimport timeimport pymongofrom lxml import etreefrom requests.exceptions ...
- hexo d 部署博客时出错
问题描述: // 第一次遇到的问题 Error: packet_write_wait: Connection to 192.30.253.113 port 22: Broken pipe packet ...
- Java感情
不知道怎么会执着于这一门语言.论速度的话,c那系更好一些,而且对底层的操控更多是c那边的.还是想通过Java做这些事. 对Java不怎么懂,它的底层实现是怎样的.只知道一个大体的思想,不知道具体步骤. ...
- 前端图片缓存之通过img标签加载GIF只能播放一次问题(转载)
最近项目中要求再网页中插入一张gif图片,让用户每次到达该位置时动一次,所以我们就制作了一张只动一次的gif图片通过img标签引入.当用户进入该位置时,通过remove()清除图片然后重新append ...