• 单源最短路径问题:从某固定源点出发,求其到所有其他顶点的最短路径

    • (有向)无权图:BFS
    • (有向)有权图:Dijkstra算法
  • 多源最短路径问题:求任意两顶点间的最短路径

    • 直接将单源最短路算法调用|V|遍
    • Floyd算法

1.BFS算法求解单源无权图最短路径

1.1算法描述

广度优先搜索,开一个额外的数组存储每一个结点的访问状态,一层一层(取出队首元素,遍历所有相邻且未被访问的结点)的入队列,然后层数++

这里的额外数组就是dist[w],指的是从源点到顶点w的最短路径长度,初始化为-1,判断未访问即==-1,如果未访问且存在边G[v][w]则dist[w] = dist[v] +1 ;

path数组用于保存每一个顶点w的前驱顶点v,也即这条最短路径(s->w)必定是从(s->....->v->w),通过栈来逆序输出path[w] 、path[path[w]]....

更加详细的算法示例可以参考视频

1.2代码实现

#include<iostream>
#include<stdlib.h>
#include<cstdlib>
#include<queue>
#include<stack>
#define Init -1
#define MaxVertex 100
int path[MaxVertex]; // 存储路径,如果当前顶点v出队列,且存在顶点v->w的路径,则path[w] = v
int dist[MaxVertex]; // 存储路径长度,即从源顶点s到当前顶点w的最短路径dist[w]
int G[MaxVertex][MaxVertex]; // 图,采用邻接矩阵表示
int Ne; // 顶点数
int Nv; // 边
typedef int Vertex;
using namespace std; void build(){
int v1,v2;
// 初始化点
cin>>Nv;
for(int i=1;i<=Nv;i++)
for(int j=1;j<=Nv;j++)
G[i][j] = 0;
// 初始化路径
for(int i=1;i<=Nv;i++)
path[i] = Init;
// 初始化路径长度
for(int i=1;i<=Nv;i++)
dist[i] = Init;
// 初始化边
cin>>Ne;
for(int i=0;i<Ne;i++){
cin>>v1>>v2;
G[v1][v2] = 1; // 有向图!
}
} void Unweighted(Vertex v){
queue<Vertex> q;
dist[v] = 0; // 将自己的距离置 0 ,路径path[v]不变
Vertex w;
q.push(v);
while(!q.empty()){
w = q.front();
q.pop();
for(int i=1;i<=Nv;i++)
// 如果没被访问过,且连通
if(dist[i]==Init && G[w][i]){
dist[i] = dist[w]+1; // 是上一步的距离 + 1
path[i] = w; // w 是上一步要走路径的下一步路径
q.push(i);
}
}
} // 获取路径
void getTail(Vertex v){
for(int i=1;i<=Nv;i++){
if(i==v)
continue;
stack<Vertex> s;
cout<<v<<"到"<<i<<"的最短距离是:"<<dist[i];
Vertex w = i;
// 当没到达起始起点前一直做循环
while(path[w]!=Init){
s.push(w); // 入栈
w = path[w];
}
// 逆序输出入栈元素,得到路径
cout<<" 其路径为:";
if(v != i)
cout<<v;
while(!s.empty()){
// 输出栈顶元素
cout<<"→"<<s.top();
s.pop(); // 出栈
}
cout<<endl;
}
} int main(){
build();
Unweighted(3);
getTail(3);
return 0;
}

2.Dijkstra算法求解单源有权图最短路径

2.1算法描述

有权图的单源最短路算法可以使用Dijkstra算法实现,Dijkstra算法的基本思想是对图G(V,E)设置集合S,存放已被访问的顶点,然后每次从集合V-S中选择与起点s的最短距离最小的一个顶点(记为u),访问并加入集合S。之后,令顶点u为中间点,优化所有起点s通过点u能够到达的邻接点v之间的最短路径。这样的操作执行n次,直到集合S已包含所有顶点。

算法的伪码描述如下:

void Dijkstra( Vertex s ) {
while (1) {
V = 未收录顶点中dist最小者;
if ( 这样的V不存在)
break;
collected[V] = true;
for ( V 的每个邻接点W )
if ( collected[W] == false )
if ( dist[V]+E<V,W> < dist[W] ) {
dist[W] = dist[V] + E<V,W> ;
path[W] = V;
}
}
} /* 不能解决有负边的情况*/

引出了两个问题:

  • 如何确定未收录顶点中dist最小者?
  • 如何初始化dist[i]?

如何确定未收录顶点中dist最小者?

1.直接扫描所有未收录顶点,时间复杂度为– O( |V| ),总的时间复杂度为T = O( |V|^2 + |E| )对于稠密图效果好

2.将dist存在最小堆中,时间复杂度为– O( log|V| ),总的时间复杂度为T = O( |V| log|V| + |E| log|V| ) = O( |E| log|V| ),对于稀疏图效果好。

如何初始化dist[i]?

  • 对于dist[0],也就是源点可以直接初始化为0
  • 对于存在边G[0][w],则dist[w]可以直接初始化为顶点s到顶点w的边权
  • 其它的顶点w,初始化为infinity(无穷大)

  • 初始状态,两个数组的初始化如上图所示

对于算法的详细示例可以参考视频

2.2代码实现

#include<iostream>
#include<stdlib.h>
#define Inf 1000000
#define Init -1
#define MaxVertex 100
typedef int Vertex;
int G[MaxVertex][MaxVertex];
int dist[MaxVertex]; // 距离
int path[MaxVertex]; // 路径
int collected[MaxVertex]; // 被收录集合
int Nv; // 顶点
int Ne; // 边
using namespace std; // 初始化图信息
void build(){
Vertex v1,v2;
int w;
cin>>Nv;
// 初始化图
for(int i=1;i<=Nv;i++)
for(int j=1;j<=Nv;j++)
G[i][j] = 0;
// 初始化路径
for(int i=1;i<=Nv;i++)
path[i] = Init;
// 初始化距离
for(int i=0;i<=Nv;i++)
dist[i] = Inf;
// 初始化收录情况
for(int i=1;i<=Nv;i++)
collected[i] = false;
cin>>Ne;
// 初始化点
for(int i=0;i<Ne;i++){
cin>>v1>>v2>>w;
G[v1][v2] = w; // 有向图
}
} // 初始化距离和路径信息
void crate(Vertex s){
dist[s] = 0;
collected[s] = true;
for(int i=1;i<=Nv;i++)
if(G[s][i]){
dist[i] = G[s][i];
path[i] = s;
}
} // 查找未收录顶点中dist最小者
Vertex FindMin(Vertex s){
int min = 0; // 之前特地把 dist[0] 初始化为正无穷
for(Vertex i=1;i<=Nv;i++)
if(i != s && dist[i] < dist[min] && !collected[i])
min = i;
return min;
} void Dijkstra(Vertex s){
crate(s);
while(true){
Vertex V = FindMin(s); // 找到
if(!V)
break;
collected[V] = true; //收录
for(Vertex W=1;W<=Nv;W++)
if(!collected[W] && G[V][W]){ // 如果未被收录
if(dist[V] + G[V][W] < dist[W]){
dist[W] = G[V][W] + dist[V];
path[W] = V;
}
}
}
} void output(){
for(int i=1;i<=Nv;i++)
cout<<dist[i]<<" ";
cout<<endl;
for(int i=1;i<=Nv;i++)
cout<<path[i]<<" ";
cout<<endl;
} int main(){
build();
Dijkstra(1);
output();
return 0;
}

3.Floyd算法求解多源最短路径算法

#include<iostream>
#include<stdlib.h>
#define INF 1000000
#define MaxVertex 100
typedef int Vertex;
int G[MaxVertex][MaxVertex];
int dist[MaxVertex][MaxVertex]; // 距离
int path[MaxVertex][MaxVertex]; // 路径
int Nv; // 顶点
int Ne; // 边
using namespace std; // 初始化图信息
void build(){
Vertex v1,v2;
int w;
cin>>Nv;
// 初始化图
for(int i=1;i<=Nv;i++)
for(int j=1;j<=Nv;j++)
G[i][j] = INF;
cin>>Ne;
// 初始化点
for(int i=0;i<Ne;i++){
cin>>v1>>v2>>w;
G[v1][v2] = w;
G[v2][v1] = w;
}
} void Floyd(){
for(Vertex i=1;i<=Nv;i++)
for(Vertex j=1;j<=Nv;j++){
dist[i][j] = G[i][j];
path[i][j] = -1;
}
for(Vertex k=1;k<=Nv;k++)
for(Vertex i=1;i<=Nv;i++)
for(Vertex j=1;j<=Nv;j++)
if(dist[i][k] + dist[k][j] < dist[i][j]){
dist[i][j] = dist[i][k] + dist[k][j];
path[i][j] = k;
}
} void output(){
for(Vertex i=1;i<=Nv;i++){
for(Vertex j=1;j<=Nv;j++)
cout<<dist[i][j]<<" ";
cout<<endl;
}
cout<<endl;
for(Vertex i=1;i<=Nv;i++){
for(Vertex j=1;j<=Nv;j++)
cout<<path[i][j]<<" ";
cout<<endl;
}
} int main(){
build();
Floyd();
output();
return 0;
}

更多详细的算法描述请参考视频

以及文章

【algo&ds】7.最短路径问题的更多相关文章

  1. 【algo&ds】4.B树、字典树、红黑树、跳表

    上一节内容[algo&ds]4.树和二叉树.完全二叉树.满二叉树.二叉查找树.平衡二叉树.堆.哈夫曼树.散列表 7.B树 B树的应用可以参考另外一篇文章 8.字典树Trie Trie 树,也叫 ...

  2. 【algo&ds】8.最小生成树

    1.最小生成树介绍 什么是最小生成树? 最小生成树(Minimum spanning tree,MST)是在一个给定的无向图G(V,E)中求一棵树T,使得这棵树拥有图G中的所有顶点,且所有边都是来自图 ...

  3. 【algo&ds】2.线性表

    1.线性表 线性表(英语:Linear List)是由n(n≥0)个数据元素(结点)a[0],a[1],a[2]-,a[n-1]组成的有限序列. 其中: 数据元素的个数n定义为表的长度 = " ...

  4. 【algo&ds】【吐血整理】4.树和二叉树、完全二叉树、满二叉树、二叉查找树、平衡二叉树、堆、哈夫曼树、B树、字典树、红黑树、跳表、散列表

    本博客内容耗时4天整理,如果需要转载,请注明出处,谢谢. 1.树 1.1树的定义 在计算机科学中,树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结 ...

  5. 【algo&ds】3.栈和队列

    1.堆栈 堆栈(Stack):具有一定操作约束的线性表(只在一端(栈顶,Top)做插入.删除) 先进后出特性 1.1堆栈的抽象数据类型描述 类型名称: 堆栈(Stack) 数据对象集:一个有0个或多个 ...

  6. 【algo&ds】1.时间复杂度和空间复杂度分析

    1.时间复杂度分析O(f(n)) 分析方法 只关注循环执行次数最多的一段代码 加法原则 乘法原则 高优先级原则 常见时间复杂度量级 多项式量级和非多项式量级.其中,非多项式量级只有两个:O(2^n) ...

  7. 【algo&ds】0.数据结构和算法入门

    解决问题方法的效率,跟数据的组织方式有关 解决问题方法的效率,跟空间的利用效率有关 解决问题方法的效率,跟算法的巧妙程度有关 什么是数据结构 数据对象在计算机中的组织方式 逻辑结构 物理存储结构 数据 ...

  8. 【algo&ds】9.拓扑排序、AOV&AOE、关键路径问题

    对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性 ...

  9. 【algo&ds】6.图及其存储结构、遍历

    1.什么是图 图表示"多对多"的关系 包含 一组顶点:通常用 V(Vertex)表示顶点集合 一组边:通常用 E(Edge)表示边的集合 边是顶点对:(v,w)∈ E,其中 v,w ...

随机推荐

  1. ThinkPHP<6.0 SQL注入代码审计分析

    版本过多只分析大版本和使用人数较多的版本目前使用人数最多的3.2.3.审计时也是发现多个版本未公开漏洞 测试环境:  Mysql5.6/PHP5.5 首先明确的是在不使用PDO做参数绑定时ThinkP ...

  2. java学习3-流程控制与数组

    1.顺序结构 2.分支结构 3.循环结构 4.控制循环结构 break continue return 5.数组

  3. .NET Core 3.0之深入源码理解ObjectPool(二)

    写在前面 前文主要介绍了ObjectPool的一些理论基础,本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的.下图为其三大核心组件图: 核心组件 Obj ...

  4. 近万字案例:Rancher + VMware PKS实现全球数百站点K8S集群管理

    Sovereign Systems是一家成立于2007年的技术咨询公司,帮助客户将传统数据中心技术和应用程序转换为更高效的.基于云的技术平台,以更好地应对业务挑战.曾连续3年提名CRN,并且在2012 ...

  5. 远程控制服务(SSH)之Linux环境下客户端与服务端的远程连接

    本篇blog将讲述sshd服务提供的两种安全验证的方法,并且通过这两种方法进行两台Linux虚拟机之间的远程登陆. 准备工作: (1)     准备两台安装有Linux系统的虚拟机,虚拟机软件采用VM ...

  6. 微信小程序和微信小程序之间的跳转和传参示例代码附讲解

    一:微信小程序跳转 使用限制 需要用户触发跳转 从 2.3.0 版本开始,若用户未点击小程序页面任意位置,则开发者将无法调用此接口自动跳转至其他小程序. 需要用户确认跳转 从 2.3.0 版本开始,在 ...

  7. 设计模式(十二)Decorator模式

    Decorator模式就是不断地为对象添加装饰的设计模式.以蛋糕为例,程序中的对象就相当于蛋糕,然后像不断地装饰蛋糕一样地不断地对其增加功能,它就变成了使用目的更加明确的对象. 首先看示例程序的类图. ...

  8. Udp 异步通信(三)

    转自:https://blog.csdn.net/zhujunxxxxx/article/details/44258719 1)服务端 using System; using System.Colle ...

  9. vue-music 使用better-scroll遇到轮播图不能自动轮播

    根据vue-music视频中slider组建的使用,当安装新版本的better-scroll,轮播组件,不能正常轮播 这是因为,better-scroll发布新版本之后,参数设置发生改变 这是旧版本: ...

  10. 【C#多线程】1.Thread类的使用及注意要点

    Thread随便讲讲 因为在C#中,Thread类在我们的新业务上并不常用了(因为创建一个新线程要比直接从线程池拿线程更加耗费资源),并且在.NET4.0后新增了Task类即Async与await关键 ...