Dijkstra算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径

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

  • 邻接矩阵实现Dijkstra(邻接矩阵其实就是代码中的maps数组)
    Dijkstra算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径_第2张图片
#include
#include
#include
#include 
#include
#include
#include 
#include
#include 
#include 
#include
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
#define gr greater
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算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径_第3张图片
#include
#include
#include
#include 
#include
#include
#include 
#include
#include 
#include 
#include
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
#define gr greater
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算法及其邻接矩阵和邻接表的实现+邻接表和图论实现单源最短路径_第4张图片
#include
#include
#include
#include 
#include
#include
#include 
#include
#include 
#include 
#include
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
#define gr greater
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;
}


你可能感兴趣的:(C++)