数学建模之图论最短路径问题

图论最短路径问题

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之内的所有节点及距离
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值