图论最短路径问题
- 在线作图:http://csacademy.com/app/graph_editor/ 国外网站加载较慢
Matlab图论实现
- 无向图
%(1)无权重(每条边的权重默认为1)
%函数graph(s,t):在s和t中对应的节点之间创建边,并生成一个图
%s,t都必须具有相同的元素数,这些节点必须都是从1开始的正整数,或都是字符串元胞数组。
%注意:数字编号最好从1开始连续编号,不然会出现孤立点
s=[1,2,3,4];
t=[2,3,1,1];
G=graph(s,t);
plot(G);
%字符串元胞数组建立,要用大括号
s={'school','home','hotel','KTV'};
%设置线的宽度
plot(G,'linewidth',2);
%画图后不显示坐标
set(gca,'XTick',[],'YTick',[])
%(2)有权重
%函数graph(s,t,w):在s,t中的对应节点之间以w的权重创建边,并生成一个图
s2=[1,2,3,4];
t2=[2,3,1,1];
w2=[3,8,9,3];
G2=graph(s2,t2,w2);%直接plot(G2)是没有效果的
plot(G2,'EdgeLabel',G2.Edges.Weight)
- 有向图
无权图 函数digraph(s,t);
有权图 函数digraph(s,t,w);
语法与函数graph()一样。
迪杰斯特拉算法(Dijkstra)
- 不能用于存在负权重的图
贝尔曼-福特算法(Bellman-Ford)
- 贝尔曼-福特算法不再将节点区分为是否已访问的状态,因为贝尔曼-福特模型是利用循环来进行更新距离的,且每循环一次,贝尔曼-福特算法都会更新所有节点的信息。
- 复杂度比Dijkstra算法高,效率更低
- 可以计算负权边,但不是负权回路。
Java实现方法
import java.util.HashMap;
/*
贝尔曼-福特算法(Bellman-Ford)
author:luffy
*/
public class main {
public static void main(String[] args) {
//创建图
Edge ab = new Edge("a", "b", 3);
Edge ae = new Edge("a", "e", 9);
Edge bf = new Edge("b", "f", 1);
Edge cd = new Edge("c", "d", 3);
Edge ec = new Edge("e", "c", 4);
Edge ed = new Edge("e", "d", 9);
Edge ef = new Edge("e", "f", 7);
//从出发点开始,依次添加经过1、2、3...个点的边,为了接下来遍历的顺序
Edge[] edges = new Edge[]{ab, ae, bf, ef,ec,ed,cd};
//存放从出发点到各个点的距离
HashMap<String, Double> costMap = new HashMap<String, Double>();
//存放各个节点对应的父节点,为了显示最短路径
HashMap<String, String> parentMap = new HashMap<String, String>();
//初始化数据
costMap.put("a", 0.0); //出发点
costMap.put("b", Double.MAX_VALUE);
costMap.put("c", Double.MAX_VALUE);
costMap.put("d", Double.MAX_VALUE);
costMap.put("e", Double.MAX_VALUE);
costMap.put("f", Double.MAX_VALUE);
//在Bellman-Ford算法中,对节点进行至多n-1次遍历即可得到最短路径,除非存在负权回路
//进行节点数n-1次循环
for (int i = 1; i < costMap.size(); i++) {
boolean hasChanged = false;
//遍历所有边
for (Edge edge : edges) {
//包含了初始化数据,所以要判断是否为null
//这里的距离都是指到出发点的距离
double startPointCost = costMap.get(edge.getStartPoint()) == null ? 0.0 : costMap.get(edge.getStartPoint());
double endPointCost = costMap.get(edge.getEndPoint()) == null ? Double.MAX_VALUE : costMap.get(edge.getEndPoint());
//如果这条边的开始点加上边的权重比结束点到出发点的距离小,则说明有更短的路径
if (startPointCost + edge.getWeight() < endPointCost) {
//更新costMap中到出发点的距离
costMap.put(edge.getEndPoint(), startPointCost + edge.getWeight());
//在parentMap中添加该边的开始点为结束点的父节点
parentMap.put(edge.getEndPoint(), edge.getStartPoint());
hasChanged = true;
}
}
//如果遍历了所有边后没有更新,那么说明已经求出了解,一般情况下都会提前结束
if (!hasChanged) break;
}
//再进行一次遍历,判断是否存在负权回路,如果存在负权回路,则还会继续更新
boolean hasNegative = false;
for (Edge edge : edges) {
double startPointCost = costMap.get(edge.getStartPoint()) == null ? 0 : costMap.get(edge.getStartPoint());
double endPointCost = costMap.get(edge.getEndPoint()) == null ? Integer.MAX_VALUE : costMap.get(edge.getEndPoint());
if (startPointCost + edge.getWeight() < endPointCost) {
System.out.println("存在负权边,无法求解");
hasNegative = true;
break;
}
}
//打印出出发点到各个节点的最短路径
if (!hasNegative) {
for (String key : costMap.keySet()) {
System.out.print("到节点"+key+"最短距离为:"+costMap.get(key)+",");
if(parentMap.containsKey(key)){
String path="";
String parentKey=parentMap.get(key);
while (parentKey!=null){
path=parentKey+" "+path;
parentKey=parentMap.get(parentKey);
}
System.out.println("路线为:"+path+"\n");
}
}
}
}
}
class Edge {
private String startPoint;
private String endPoint;
private double weight;
public Edge(String start, String end, double weight) {
this.startPoint = start;
this.endPoint = end;
this.weight = weight;
}
public String getStartPoint() {
return startPoint;
}
public String getEndPoint() {
return endPoint;
}
public double getWeight() {
return weight;
}
}
Matlab计算最短路径
[P,d] = shortestpath(G,start,end [,‘Method’,algorithm])
- 功能:返回图G中start节点到end节点的最短路径
- 输入参数:
1.G-输入图(graph对象|digraph对象)
2.start起始节点
3.end目标节点
4.[,‘Method’,algorithm]是可选参数,表示计算最短路径的算法。一般可省略,默认使用"auto",具体参数见下表。 - 输出参数:
1.P-最短路径经过的节点集
2.d-最短距离
可选算法
选项 | 说明 |
---|---|
‘auto’(默认值) | 会自动选择算法 |
‘unweighted’ | 广度优先计算,将所有边权重都视为1 |
‘positive’ | Dijkstra算法,要求所有边权重均为非负数 |
‘mixed’(仅适用于digraph) | 适用于有向图的Bellman-Ford算法,要求图没有负循环 |
Matlab示例代码
[P,d]=shortestpath(G,start,end);
%在图中高亮最短路径
my_plot=plot(G,'EdgeLabel',G.Edges.Weight,'linewidth',2);%先将图赋给一个变量
highlight(my_plot,P,'EdgeColor','r');%高亮显示
图中任意两点的最短距离矩阵
函数distance(G)
D=distance(G)
D(1,2)%1->2的最短路径
找出给定范围内的所有点
函数nearest(G,s,d)
[nodes,dist]=nearest(G,s,d);
%返回图G中与节点s的距离在d之内的所有节点及距离