Dijkstra算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径
程序员文章站
2022-03-25 17:59:50
...
Dijkstra算法+邻接矩阵+邻接表+图论实现单源最短路径问题求解
- 这里的是有向图,即两点路径不可逆,无向图类似,不再赘诉。
这里一共介绍四块内容,打字太累,直接用笔记本拍摄的图片,笔记本上写的很细了,外附代码和测试用例。
这几个主要的区别是:
1.邻接矩阵:二维数组,n大的时候没法用,太耗空间,而且每次更新路径都要从1~n,耗时。
2.邻接表:一维数组,省空间,next下一个邻接节点,无需从1~n,相对多数情况省时,当然邻接矩阵特殊处理就不算在内。
3.图论(DFS):邻接表的基础,但是不需要更新路径长度,因为本身就是一种搜索,而且代码更简单也更容易理解。但是前面两个都是求单源到多个终点的最短路径,而这个只能1对1,求单源到单个终点。
三种哪种好要分实际情况使用。
- 测试样例
输入n和m,编号从1~n的点,m条边,接下来输入m条信息,u,v,ww,u是有向边起点,v是终点,ww是权值。最后输入x和y,代表所求路径的起点和终点。
输入:
7 10
1 2 10
1 3 15
1 5 10
2 4 20
5 4 5
4 6 10
6 7 20
1 7 50
1 6 80
3 6 30
1 6
输出:
result: 1->5->4->6 = 25
- Dijkstra算法原理
- 邻接矩阵实现Dijkstra(邻接矩阵其实就是代码中的maps数组)
#include<iostream>
#include<fstream>
#include<math.h>
#include <algorithm>
#include<stdio.h>
#include<string.h>
#include <fstream>
#include<stack>
#include <map>
#include <ctime>
#include<queue>
using namespace std;
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define req(i, a, b) for(int i=(a); i<=(b); i++)
#define ull unsigned __int64
#define sc(t) scanf("%d",&(t))
#define sc2(t,x) scanf("%d%d",&(t),&(x))
#define pr(t) printf("%d\n",(t))
#define pf printf
#define prk printf("\n")
#define pi acos(-1.0)
#define ms(a,b) memset((a),(b),sizeof((a)))
#define mc(a,b) memcpy((a),(b),sizeof((a)))
#define w while
#define vr vector<int>
#define gr greater<int>
typedef long long ll;
//next_permutation(arr, arr+size);全排列
//prev_permutation(arr, arr+size);
const int N = 105, inf = 0x3f3f3f3f;
int d[N], vis[N];//vis=1的即表示已加入S集合,为01意味着还在V-S集合,仍可被选中
int maps[N][N];
int main()
{
int n, m;
sc2(n, m);
int u, v, ww;
//map记录u,v权值,不可达为inf,即无穷,自己到自己为0
ms(maps, inf);
req(i, 1, n)
maps[i][i] = 0;
req(i, 1, m)
{
sc2(u, v);
sc(ww);
maps[u][v] = ww;
}
int x, y;
sc2(x, y); //起始和终止结点
req(i, 1, n)
d[i] = maps[x][i]; //即由起始点开始,到各点的距离
vis[x] = 1;//标记起始点已加入s集合
req(i, 1, n-1) //松弛的是边,只需要n-1遍
{
int mi = inf, k;
req(j, 1, n)
{
if(!vis[j] && d[j] < mi) //小于inf代表有边存在
{
mi = d[j];
k = j; //从现存V-S集中,选取与起始结点有最短路径的结点
}
}
vis[k] = 1; //从V-S中加入到S集去
req(r, 1, n)
{
if(maps[k][r] < inf) //即与新加入结点间有边,只有与新节点有边,与起始节点的路径长度才有可能被更新
if(d[k]+maps[k][r] < d[r]) //更新
d[r] = d[k]+maps[k][r];
}
}
//到此为止,已经更新了起始结点到各节点的最短路径,此处没有指向性,不能指定最终结点,邻接表与图论的结合才可以
//因此如是写
//req(i, 1, n) cout << d[i] << " "; 即输出到各点最短路长度
cout << d[y] << endl; //即输出到指定点长度。
return 0;
}
- 邻接表实现Dijkstra(数组模拟,不是用链表,其实数组能看懂比链表简洁很多)
#include<iostream>
#include<fstream>
#include<math.h>
#include <algorithm>
#include<stdio.h>
#include<string.h>
#include <fstream>
#include<stack>
#include <map>
#include <ctime>
#include<queue>
using namespace std;
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define req(i, a, b) for(int i=(a); i<=(b); i++)
#define ull unsigned __int64
#define sc(t) scanf("%d",&(t))
#define sc2(t,x) scanf("%d%d",&(t),&(x))
#define pr(t) printf("%d\n",(t))
#define pf printf
#define prk printf("\n")
#define pi acos(-1.0)
#define ms(a,b) memset((a),(b),sizeof((a)))
#define mc(a,b) memcpy((a),(b),sizeof((a)))
#define w while
#define vr vector<int>
#define gr greater<int>
typedef long long ll;
//next_permutation(arr, arr+size);全排列
//prev_permutation(arr, arr+size);
const int N=105, inf=0x3f3f3f3f;
int u[N], v[N], ww[N], fi[N], ne[N], d[N], vis[N];
int main()
{
int n, m;
int x, y, s;
sc2(n, m);
req(i, 1, n)
{
fi[i] = -1;
d[i] = inf;
}
req(i, 1, m)
{
sc2(x,y);
sc(s);
u[i] = x;
v[i] = y;
ww[i] = s;
ne[i] = fi[x];
fi[x] = i;
//如有3条边 1->2, 1->3, 1->5,则ne[1]=fi[1]=-1,fi[1]=1;
//ne[2]=fi[1]=1,fi[1]=2; ne[3]=fi[1]=2,fi[1]=3;
//这样正好接成以1为链表头,fi[1]=3,ne[3]=2,ne[2]=1,ne[1]=-1;
//即[1]-> 3->2->1 后面的3-2-1是数组下标,v[3] v[2] v[1] 对应 5 3 2
}
sc2(x, y);//起点 && 终点
for(int i=fi[x]; i!=-1; i=ne[i])
d[v[i]] = ww[i]; //以x为起点的各终点y上的权值。
vis[x] = 1; //起点标记
for(int i=1; i<=n-1; i++) //因为松弛是边数,所以是n-1
{
int mi = inf, k;
for(int j=1; j<=n; j++)
{
if(!vis[j] && d[j] < mi)
{
mi = d[j];
k = j; //记录每一遍过程中距离起始点x最近的点
}
}
vis[k] = 1;
for(int r=fi[k]; r!=-1; r=ne[r])
{
if(ww[r] < inf)
if(d[k]+ww[r] < d[v[r]])
d[v[r]] = d[k]+ww[r];
}
}
cout << d[y] << endl;
return 0;
}
- 邻接表+DFS(节省空间)
#include<iostream>
#include<fstream>
#include<math.h>
#include <algorithm>
#include<stdio.h>
#include<string.h>
#include <fstream>
#include<stack>
#include <map>
#include <ctime>
#include<queue>
using namespace std;
#define rep(i, a, b) for(int i=(a); i<(b); i++)
#define req(i, a, b) for(int i=(a); i<=(b); i++)
#define ull unsigned __int64
#define sc(t) scanf("%d",&(t))
#define sc2(t,x) scanf("%d%d",&(t),&(x))
#define pr(t) printf("%d\n",(t))
#define pf printf
#define prk printf("\n")
#define pi acos(-1.0)
#define ms(a,b) memset((a),(b),sizeof((a)))
#define mc(a,b) memcpy((a),(b),sizeof((a)))
#define w while
#define vr vector<int>
#define gr greater<int>
typedef long long ll;
//next_permutation(arr, arr+size);全排列
//prev_permutation(arr, arr+size);
const int N=105, inf=0x3f3f3f3f;
int u[N], v[N], ww[N], fi[N], ne[N], d[N], vis[N];
int ans = inf;
void dfs(int x, int y, int len)
{
if(x == y)
{
ans = min(ans, len);
}
for(int i=fi[x]; i!=-1; i=ne[i])
{
if(!vis[v[i]])
{
vis[v[i]] = 1;
dfs(v[i],y,len+ww[i]);
vis[v[i]] = 0;
}
}
}
int main()
{
int n, m;
int x, y, s;
sc2(n, m);
req(i, 1, n)
{
fi[i] = -1;
d[i] = inf;
}
req(i, 1, m)
{
sc2(x,y);
sc(s);
u[i] = x;
v[i] = y;
ww[i] = s;
ne[i] = fi[x];
fi[x] = i;
//如有3条边 1->2, 1->3, 1->5,则ne[1]=fi[1]=-1,fi[1]=1;
//ne[2]=fi[1]=1,fi[1]=2; ne[3]=fi[1]=2,fi[1]=3;
//这样正好接成以1为链表头,fi[1]=3,ne[3]=2,ne[2]=1,ne[1]=-1;
//即[1]-> 3->2->1 后面的3-2-1是数组下标,v[3] v[2] v[1] 对应 5 3 2
}
sc2(x, y);//起点 && 终点
vis[x] = 1;
dfs(x, y, 0);
cout << ans << endl;
return 0;
}