欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径

  • 邻接矩阵实现Dijkstra(邻接矩阵其实就是代码中的maps数组)
    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 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(数组模拟,不是用链表,其实数组能看懂比链表简洁很多)
    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(节省空间)
    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 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;
}