贪婪算法-单源最短路径

撸了今年阿里、头条和美团的面试,我有一个重要发现.......>>> hot3.png

前言

感谢每一位朋友的阅读与建议,今天对最短路径blog进行了修改,调整图和部分内容。感谢各位关注。提早祝大家圣诞节平安快乐。

单源最短路径问题描述

给定一个带权有向图G=(V,E),其中每条边的权是一个实数。另外,还给定V中的一个顶点,称为源。现在要计算从源到其他所有各顶点的最短路径长度。这里的长度就是指路上各边权之和。这个问题通常称为单源最短路径问题

1.无权最短路径(非唯一)

算法分析

  1. 由于图没有权,所以我们只需要关注路径上的边
  2. 无权最短路径实质上是特殊的有权最短路径,因为我们可以将每条边按权为1处理。
  3. 我们可以一层一层处理,先找与s距离为1的节点,之后找距离为2的节点,直到所有节点都被访问到。 注: 按层搜索图的方式,称为广度优先搜索,这种搜索方式类似树的层序遍历。

算法描述

借助队列实现每条边只访问一次。

  1. 初始情况下声明所有节点的最短路径未知
  2. 起点s声明最短路径为0,并将s入队。
  3. 从队列中移除一个节点v ,并更新该点v的临接表wlist中每一个临接点w的最短路径为当前最短路径dv+1
  4. 重复1-3步骤 ,直到队列为空为止。

图解说明

无权最短路径

核心代码

/**
  * 无权最短路径
  * 
  * @param s 起点
  */
public void unweight(Vertex s) {
	Queue<Vertex> q = new LinkedList<Vertex>();
	for (Vertex x : graph) {
	 //每个节点的初始最短路径为Integer的最大值,表示该节点的最短路径未知
	  x.setDist(Integer.MAX_VALUE);
	}
	s.dist = 0;
	q.add(s);
	while (!q.isEmpty()) {
		Vertex v = q.poll();
	  if (v != null) {
		if (v.getAdj() != null && !v.getAdj().isEmpty()) {
		  for (Vertex w : v.getAdj()) {
			if (w.getDist() == Integer.MAX_VALUE) {//每条边只访问一次
				w.setDist(v.getDist()+1);
				w.setPath(v);
				q.add(w);
			}
		  }
	    }
     }
   }
}

该算法的时间界限

O(|E|+|V|)

2.有权无负值最短路径

Dijkstra算法是解决有权无负值单源最短路径的经典算法。

Dijkstra算法描述

  1. 选择一个未知最短路径的节点v,它在所有未知最短路径的节点集中有最小的路径dv。
  2. 声明v为已知最短路径节点
  3. 更新v的临接顶点集,针对每个v的临接点w,若dv+cvw<dw,则更新w的路径。
    注:cvw为边(v,w)的权,dv,dw分别为v,w的最短路径
  4. 重复1-3步骤,直到所有顶点的最短路径都已知。

图解说明

Dijkstra算法1

Dijkstra算法2

核心代码

     /**
	 * 著名的dijkstra算法 解决单源最短路径(权无负值)
	 * 
	 * @param s
	 *            起点
	 */
public void dijkstra(Vertex s) {
	for (Vertex v : graph) {// 初始默认所有顶点未被访问
		v.setDist(Integer.MAX_VALUE);
		v.known = false;
	}
	s.dist = 0;// 声明起点的距离为0
	PriorityQueue<Vertex> priorityQueue = new PriorityQueue<Vertex>();
	// 将s放入优先队列
	priorityQueue.add(s);
	while (!priorityQueue.isEmpty()) {// 知道所有顶点的最短路径都已知并且优先队列为空
		Vertex v = priorityQueue.poll();// 取出未知节点中最短路径最小的节点
		if (v != null) {
			if (!v.known) {
				v.known = true;// 声明该节点已知
				if (v.getAdj() != null && !v.getAdj().isEmpty()) {
				// 如 dv+cvw<=dw 则更新临接点的最短路径,并且更新值放入到优先队列中
				for (AdjVertex adjW : v.getAdj()) {
				if (!adjW.getW().known) {
					if (v.dist + adjW.cvw < adjW.getW().getDist()) {
					  adjW.getW().setDist(v.dist + adjW.cvw);
					  adjW.getW().setPath(v);
					  priorityQueue.add(adjW.getW());
								}
							}
						}
					}
				}
			}
		}
	}

3.有权有负值无圈最短路径(非唯一)

问题定义

针对一个有权图,该图的权有负值,使用某个顶点s作为输入参数,找出该顶点s到其他顶点的最短距离。

为何不能使用Dijkstra算法

  1. Dijkstra有可能过早的声明一个节点的最短路径已知,由于有权有负值存在,可能还有一条含有负值边的路径经过该节点,使得该节点的最短路径更小。
  2. 利用一个修正值,将图的边修正为正数之后使用Dijkstra,也是不行的 ,因为含有较多边的路径会被过度修正,如下图所示:

图解说明“修正负值出现的问题”

 

有权有负值无圈最短路径的解法

借助广度优先搜素实现。

  1. 将起点放入队列
  2. 从队列中取出节点v,更新v的临接顶点集,针对每个v的临接点w,若dv+cvw<dw,则更新w的路径。
    注:cvw为边(v,w)的权,dv,dw分别为v,w的最短路径
  3. 当w不在队列中时,将w放入队列
  4. 直到队列为空为止

核心代码

	/**
	 * 有权有负值最短路径
	 * 借助广度优先搜素
	 * @param s 起点
	 */
	public void weightNegative(Vertex s) {
		Queue<Vertex> q = new LinkedList<Vertex>();
		for (Vertex v : graph) {
			v.dist = Integer.MAX_VALUE;
			v.isInQueue = false;
		}
		s.dist = 0;
		s.isInQueue = true;
		q.add(s);
		while (!q.isEmpty()) {
			Vertex v = q.poll();
			v.isInQueue = false;
			if (v.getAdj() != null && !v.getAdj().isEmpty()) {
				for (AdjVertex wadj : v.getAdj()) {
					if (wadj.cvw + v.dist < wadj.getW().dist) {
						wadj.getW().setDist(wadj.cvw + v.dist);
						wadj.getW().setPath(v);
						if (!wadj.getW().isInQueue) {
							wadj.getW().isInQueue = false;
							q.add(wadj.getW());
						}
					}
				}
			}
		}
	}

时间界限

O(|E|*|V|)

总结与补充

  1. 上述算法,若图有圈,都不能执行,上述算法是以临接表的方式标示图的。
  2. 无权最短路径借助广度优先搜素实现,其时间界限为:
O(|E|+|V|)
  1. Dijkstra是解决有权无负值图单源最短路径的经典算法。

  2. 若权有负值,借助广度优先搜素与有权无负值最短路径思想结合来解决 ,其时间界限为:

O(|E|*|V|)

完整代码地址

码云地址

无权无圈最短路径

有权无负值最短路径

有权有负值最短路径

github地址

无权无圈最短路径

有权无负值最短路径

有权有负值最短路径

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值