一、Dijkstra算法(单源最短路径算法)
狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。
要计算非加权图中的最短路径,可使用广度优先搜索。要计算加权图中的最短路径,可使用狄克斯特拉算法。在无向图中,每条边都是一个环。狄克斯特拉算法只适用有向无环(directed acyclicgraph,DAG),但是无向边也可以当成两条指向相反的有向边。Dijkstra 算法可以很好地解决无负权图的最短路径问题。
狄克斯特拉算法背后的关键理念:找出图中最便宜的节点,并确保没有到该节点的更便宜的路径!
邻接矩阵和邻接表实现的代码
#include<iostream>
#include<algorithm>
#include<vector>
const int MAXV = 1000;//最大顶点数
const int INF = 1000000000;//设INF为一个很大的数
using namespace std;
//领接矩阵版
//适用于点数不大(例如V小于1000的情况)
//初始化数据
int n;//图的顶点数
int G[MAXV][MAXV];//图的邻接矩阵,MAXV为最大顶点数
int d[MAXV]; //源点到达各点的最短路径长度
int vis[MAXV] = {false};//vis[i]==true表示已访问,初值均为false。
void Dijkstra(int s){
//u是中介点(源点到待访问节点的最短距离对应的结点标号)
//MIN保存从源点到待访问节点的最短距离
fill(d,d+MAXV,INF);
d[s] = 0; //除源点自身处的最短距离为0之外,源点到其余点的距离首先都是INF。
for(int i = 0;i<n;i++){
int u = -1,MIN = INF;
for(int j=0;j<n;j++){
//找到未访问的顶点中,d[]最小的(即从源点到该点有路径,也有距离,但此时不确定是最短的)
if(vis[j]==false && d[j]<MIN){
u = j; //u表示源点到未访问结点的最短距离对应的结点的标号
MIN = d[j];
}
}
//若找不到最短路径,u==-1,则中断返回函数
if(u==-1) return;
//将u作为中介点(开放节点),探索源点经过u到达其余结点v的路径距离是否比d[v]小,是,则更新
vis[u] = true;//标记u为已访问
for(int v=0;v<n;i++){
//如果v未访问,u能到达v且以点u为中介点可以使d[v]更优。
if(vis[v]==false && G[u][v]!=INF && d[u]+G[u][v]<d[v]){
d[v] = d[u] + G[u][v];
}
}
}
}
//邻接表版
struct Node{
int v;//v为边的目标顶点
int dis;//dis为边权
};
//图G,Adj[u]存放从顶点u出发可以到达的所有顶点
vector<Node> Adj[MAXV];//MAXV在领接矩阵中已经定义
//int n; //实际顶点数(若与邻接矩阵表示的算法分开写,则注释需要删除,下同)
//int d[MAXV]; //源点到达各点的最短路径长度
//bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问
void Dijkstra_(int s){
fill(d,d+MAXV,INF);
d[s] = 0;
for(int i=0;i<n;i++){
int MIN = INF;
int u = -1;
for(int j = 0;j<n;j++){
if(vis[j] == false && d[j]<MIN){
u = j;
MIN = d[j];
}
}
//找不到小于INF的d[u],说明剩下的顶点和起点s不连通
if(u == -1) return;
vis[u] == true; //标记u为已访问
//只有下面这个for与邻接矩阵的写法不同
for(int j=0;j<Adj[u].size();j++){
int v = Adj[u][j].v; //通过邻接表直接获得u能到达的顶点v
if(vis[v] == false && d[u]+Adj[u][j].dis < d[v]){
//如果v未访问&&以u为中介点可以使d[v]更优
d[v] = d[u] + Adj[u][j].dis; //优化d[v]
}
}
}
}
用基于邻接矩阵的Dijkstra算法和主函数进行测试
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXV = 1000; //邻接矩阵适合的最大顶点数
const int INF = 1000000000;//设置INF为一个很大的数
int n,m,s,G[MAXV][MAXV]; //n为顶点数,m为边数,s为起点
int d[MAXV]; //源点到达各点的最短路径长度
bool vis[MAXV] = {false};//标记数组,vis[i]==true表示已访问
void Dijkstra(int s){
fill(d,d+MAXV,INF);
d[s] = 0;
for(int i=0;i<n;i++){
int u = -1,MIN = INF;
for(int j=0;j<n;j++){
if(vis[j] == false && d[j]<MIN){
u = j;
MIN = d[j];
}
}
if(u==-1) return;
vis[u] = true;//标记顶点u为已访问
for(int v = 0;v<n;v++){
if(vis[v] == false && G[u][v]!=INF && d[u]+G[u][v]<d[v]){
d[v] = d[u]+G[u][v];
}
}
}
}
int main(){
int u,v,w;
cin>>n>>m>>s;//顶点个数,边数,起点编号
fill(G[0],G[0]+MAXV*MAXV,INF);
for(int i=0;i<m;i++){
cin>>u>>v>>w;
G[u][v] = w;
}
Dijkstra(s); //单源最短路径算法入口
for(int i = 0;i<n;i++){
if(i>0) cout<<" ";
cout<<d[i];
}
return 0;
}
二、Floyd算法(全源最短路径算法)
//Floyd算法(全员最短路径算法)
//时间复杂度是O(n3),决定了顶点数n的限制约在200以内,因此使用邻接矩阵实现该算法
/*
算法思想:
枚举顶点k∈[1,n]
以顶点k作为中介点,枚举所有顶点对i和j(i∈[1,n],j∈[1,n])
如果dis[i][k] + dis[k][j] <dis[i][j]成立
赋值dis[i][j] = dis[i][k]+dis[k][j]
*/
//实现代码
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXV = 200;//MAXV为最大顶点数,在该算法中,允许200个顶点。
const int INF = 1000000000;
int n,m;//n为顶点数,m为边数
int dis[MAXV][MAXV];//dis[i][j]表示顶点i和1顶点
void Floyd(){
for(int k=0;k<n;k++){
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
//松弛条件
if(dis[i][k]!=INF && dis[k][j]!=INF && dis[i][k]+dis[k][j]<dis[i][j]){
dis[i][j] = dis[i][k] + dis[k][j];//找到更短的路径
}
}
}
}
}
int main(){
int u,v,w;
fill(dis[0],dis[0]+MAXV*MAXV,INF);
cin>>n>>m;
for(int i=0;i<n;i++){
dis[i][i] = 0;//顶点i到顶点i的距离初始化为0
}
for(int i=0;i<m;i++){
cin>>u>>v>>w;
dis[u][v] = w;//以有向图为例进行输入
}
Floyd();//Floyd算法入口
for(int i=0;i<n;i++){//输出dis数组
for(int j=0;j<n;j++){
cout<<dis[i][j]<<" ";
}
cout<<endl;
}
return 0;
}