算法——图之加权有向图

这篇讨论加权有向图。

加权有向图是在有向图的基础上,边的赋予权重信息的。

加权有向图有很多应用。最典型的就是最短路径问题。我们日常生活中也经常遇到这种问题,例如从一个点出发,到达另外一个点,怎么过去才是最快的等等。

而由于图的复杂性,最短路径问题并不十分的容易。例如,给定图的边的权重可能是负权重等。


为了解决最短路径问题,我们首先要定义一种加权有向图的数据结构,良好的数据结构是成功的一半。

和之前一样,我们用邻接表矩阵的方式来存放图,链表中存放的是我们定义的数据结构的边。

如下图所示:


那么我们首先要做的就是定义边的数据结构。

如图,我们需要边的起始顶点,边的终止顶点,边的权重信息。

得到如下图的数据结构:

public class DirectedEdge {
	
	private double weight;
	private int from;
	private int to;
	
	public DirectedEdge(int from, int to, double weight) {
		this.from = from;
		this.to = to;
		this.weight = weight;
	}
	
	public int getFrom() {
		return from;
	}
	
	public int getTo() {
		return to;
	}
	
	public double weight() {
		return weight;
	}
	
	public int compareTo(DirectedEdge e) {
		if (weight > e.weight()) return 1;
		if (weight < e.weight()) return -1;
		return 0;
	}
	
	public String toString() {
		String s = from + " -> " + to + ", weight: " + weight;
		return s;
	}

}


有了边的数据结构之后,我们就可以定义加权有向图的数据结构了。

加权有向图,我们首先需要一个邻接表矩阵,还需要顶点的数量,边的数量。

得到如下数据结构:

public class EdgeWeightDiGraph {
	
	private List<DirectedEdge>[] adj; // 邻接表矩阵
	private int V; // 点的数目
	private int E; // 边的数目
	
	public EdgeWeightDiGraph(int V) {
		this.V = V;
		E = 0;
		adj = (List<DirectedEdge>[]) new List[V];
		for (int i = 0; i < V; i++) {
			adj[i] = new ArrayList<>();
		}
	}
	
	public void addEdge(DirectedEdge e) {
		adj[e.getFrom()].add(e);
		E++;
	}
	
	public int V() {
		return V;
	}
	
	public int E() {
		return E;
	}
	
	public Iterable<DirectedEdge> adj(int v) {
		return adj[v];
	}
	
	public Iterable<DirectedEdge> edges() {
		List<DirectedEdge> edges = new ArrayList<>();
		for (int i = 0; i < V; i++) {
			for (DirectedEdge e : adj[i]) {
				edges.add(e);
			}
		}
		return edges;
	}
	
	public String toString() {
		String s = V + " 个顶点, " + E + " 条边\n";
		for (int i = 0; i < V; i++) {
			s += i + ": ";
			for (DirectedEdge e : adj(i)) {
				s += e.getTo() + " [" + e.weight() + "], ";
			}
			s += "\n";
		}
		return s;
	}

}


加权有向图的数据结构我们也定义好了,那么我们就可以开始实现最短路径算法了。

首先我们来看最短路径中最出名的算法Dijkstra算法。

思路:

从起点开始,首先初始化到起点的距离为0,再初始化到其他顶点的路径为无穷大。

每次选择到达起点最近的那个顶点未被选择过的顶点,查看这个顶点作为起点的边,如果发现从这个顶点到边的另一个顶点的距离会更短,就更新它。


可以发现,每次选择到起点最近的那个没被选择过的顶点,最终会选择所有的顶点,而这些顶点的边也会被全部遍历一次。因为总是先计算距离最小的那个顶点,所以每个后来的顶点都是在前面最小路径的基础上得到的,无法更短的路径,也就是后来计算的顶点都是最短路径,这个可以用数学归纳法非常简单的理解。


那么我们就来实现Dijkstra算法。

我们发现这个算法有以下关键点:1.记录当前顶点到起点的距离 2.每次获取距离起点最近的顶点3.比较当前边到达的点的距离,如果更短就更新的操作。

1.记录当前顶点到起点的距离:这个记录十分的重要,因为我们的更新距离的操作,每次都要基于当前顶点的距离的比较结果来更新。我们使用一个double[]数组来存放。

2.每次获取距离起点最近的顶点:我们如何获取当前距离起点最近的顶点呢?最容易想到的方法就是遍历一次,这并不难实现,不过

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值