备战蓝桥杯--最短路径模板

题目

LeetCode-网络延迟时间
有 n 个网络节点,标记为 1 到 n。

给你一个列表 times,表示信号经过 有向 边的传递时间。 times[i] = (ui, vi, wi),其中 ui 是源节点,vi 是目标节点, wi 是一个信号从源节点传递到目标节点的时间。

现在,从某个节点 K 发出一个信号。需要多久才能使所有节点都收到信号?如果不能使所有节点收到信号,返回 -1 。

示例 1:

输入:times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
输出:2
示例 2:

输入:times = [[1,2,1]], n = 2, k = 1
输出:1
示例 3:

输入:times = [[1,2,1]], n = 2, k = 2
输出:-1

提示:

1 <= k <= n <= 100
1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
所有 (ui, vi) 对都 互不相同(即,不含重复边)

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/network-delay-time
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

Dijkstra+优先队列

有优先队列存储有向边。[1,2]代表着到1这个点的距离是2。g[3]={{1,2}};代表着3到1的距离是2。有数组存储边更省空间。这样存储也是为了优先队列排序后,你知道哪个值对应着哪个点。如果是下面的枚举,就直接用二维数组存储就可以了。

  • 时间复杂度:O(m log m),其中 m 是数组 times 的长度。适合稀疏图,顶点多的图。
  • 空间复杂度:O(n+m)。
class Solution {
    static public int networkDelayTime(int[][] times, int n, int k) {
		int[] distances=new int[n+1];
		ArrayList<int[]>[] g=new ArrayList[n+1];
		for(int i=1;i<n+1;i++) 
			g[i]=new ArrayList<int[]>();
		for(int[] x:times)
				g[x[0]].add(new int[] {x[1],x[2]});
		Arrays.fill(distances, 300);
		distances[k]=0;
		PriorityQueue<int[]> dis=new PriorityQueue<int[]>((a,b)->a[1]-b[1]);   //优先队列初始化
		dis.add(new int[] {k,0});   //将源点先放入队列中,然后通过循环更新周围的点
				//上面都是初始化 g中存放这所有边的信息
			//模板开始
			while(!dis.isEmpty())
			{
				int[] p=dis.poll();
                int index=p[0],p1=p[1];    //到达的点的索引
                if(distances[index]<p1)   //当前这个边没必要加入
					continue;  
				for(int[] x:g[index])      //更新index周围的点,如果值更小,则更新distances数组
				{
					int y=x[0]; int d=distances[index]+x[1];
					if(distances[y]>d)
					{
						distances[y]=d;
						dis.add(new int[] {y,d});
					}
				}
			}
			//下面和模板无关,只是题目输出
			distances[0]=-10;
			int ans=Arrays.stream(distances).max().getAsInt();
			return ans == 300 ? -1 : ans;
    }
}

在这里插入图片描述

Dijkstra+枚举

这个枚举更适合稠密图,也就是边比较多的图。而且加入了vis数组,这个数组是存放一个点是否已经找到最短路径了。
时间复杂度:O(n2+m),其中 m 是数组times 的长度。边的数量对时间影响还可以
空间复杂度:O(n2),邻接矩阵需占用 O(n2)的空间。

class Solution 
 {
    static public int networkDelayTime(int[][] times, int n, int k) {
		int INF=Integer.MAX_VALUE/2;    //因为后面会用到填充的值+另一个值,如果是最大值,那么就会变成负数。
    	boolean[] vis=new boolean[n+1];
    	int[] distances=new int[n+1];
		int[][] g=new int[n+1][n+1];
		for(int i=1;i<n+1;i++)
			Arrays.fill(g[i], INF);
		for(int[] x:times)
			g[x[0]][x[1]]=x[2];
		
		Arrays.fill(distances, 300);
		distances[k]=0;
		//模板开始
		for(int num=0;num<n;num++)
		{
			int X=-1;
			for(int i=1;i<n+1;i++)
				if(!vis[i]&&(X==-1||distances[i]<distances[X]))
					X=i;
			
			vis[X]=true;
			int min_val=distances[X];
			for(int i=1;i<n+1;i++)  //遍历和X有边的点
			{
				if(g[X][i]==INF)    //X和这个点没有边
					continue;
				//如果比原来值更小,就更新distances数组
				distances[i]=Math.min(distances[i],min_val+g[X][i]);
			}
		}
		//模板结束
			distances[0]=-10;
			int ans=Arrays.stream(distances).max().getAsInt();
			return ans == 300 ? -1 : ans;
    }  
}

在这里插入图片描述

Floyd

弗洛伊德是一种动态规划算法,稠密图效果最佳,边权可以是负数。对于稠密图,效率要高于执行Dijkstra算法。不适合大数据量计算

  • 时间复杂度:O(n3);
  • 空间复杂度:O(n2);
 class Solution 
 {
    static public int networkDelayTime(int[][] times, int n, int k) {
		int INF=Integer.MAX_VALUE/2;    
    	boolean[] vis=new boolean[n+1];
		int[][] distances=new int[n+1][n+1];
		for(int i=1;i<n+1;++i)
			for(int j=1;j<n+1;++j)
			{
				if(i==j)
					distances[i][j]=0;
				else 
					distances[i][j]=INF;
			}
		for(int[] x:times)
			distances[x[0]][x[1]]=x[2];
		//模板开始
		for(int p=1;p<n+1;p++)
			for(int i=1;i<n+1;i++)
				for(int j=1;j<n+1;j++)
					distances[i][j]=Math.min(distances[i][j], distances[i][p]+distances[p][j]);
		//模板结束
		int ans=-1;
		for(int i=1;i<n+1;i++)
			ans=Math.max(ans, distances[k][i]);
		return ans == INF ? -1 : ans;
    }  
}

在这里插入图片描述

SPFA

这里主要很有意思的一点就是邻接表实现用的4个数组。edge数组:edge[i]=5代表着索引是i的边指向的是5。 head数组:head[i]=4以点i为起点边的的下标是4。next[i]=4只的是索引为i的边的下一个边的索引是4。w数组:w[i]=5指向点i的这个边的权重是5。

  • 时间复杂度:O(n*m);
  • 空间复杂度:O(m);
 class Solution 
 {
    static public int networkDelayTime(int[][] times, int n, int k) {
		int INF=Integer.MAX_VALUE/2; 
		boolean[] vis=new boolean[n+1];
		int[] distances=new int[n+1];
		int M=6000;  //边的数量
		int index=0;
		int[] head=new int[n+1];int[] edge=new int[M];
		int[] w=new int[M];int[] next=new int[M];
		Arrays.fill(head, -1);
		Arrays.fill(next, -1);
		Arrays.fill(distances,INF);
		//存图
		for(int[] x:times)
		{
			int x0=x[0];
			w[index]=x[2];  //村上这个边的权重
			next[index]=head[x0];
			head[x0]=index;
			edge[index]=x[1];
			++index;
		}
		//模板开始
		Deque<Integer> q=new ArrayDeque<Integer>();  //两端都可以插入和删除
		q.add(k);
		distances[k]=0;
		vis[k]=true;
		while(!q.isEmpty())
		{
			int p=q.poll();
			vis[p]=false;
			for(int i=head[p];i!=-1;i=next[i])
			{
				int j=edge[i];
				if(distances[j]>distances[p]+w[i])
				{
					distances[j]=distances[p]+w[i];
					if(vis[j])
						continue;
					q.add(j);
					vis[j]=true;
				}
					
			}
		}
		//模板结束
		distances[0]=-10;
		int ans=Arrays.stream(distances).max().getAsInt();
		return ans == INF ? -1 : ans;
    }  
}

在这里插入图片描述

几种算法的比较

Dijsktra+堆优化Dijsktra+枚举FloydSPFA
时间复杂度O(m* log m)O(n2+m)O(n3)最坏O(n*m)
空间复杂度O(n+m)O(n2)O(n2)O(m)
耗时8s3s9s5s
内存45.5 Mb45.4 Mb45.2 Mb46.5 Mb
总结适合稀疏图,不能负值适合稠密图,不能负值适合稠密图,可以负值,时间复杂度太高,三个循环没有限制,可以负值是个不错的算法

这道题看起来内存差不多是因为这个图是稠密图,m接近n2。如果没有负值的图,边多用Dijsktra+枚举,顶点多边少用Dijsktra+堆优化。有负值直接SPFA,Floyd实现简单但是时空复杂度都太高了。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值