单源最短路径

单源最短路径
单源最短路径是指给定图中的一个源节点,求出从源节点到其他任意节点的最短距离及路径
针对不同的情况,这里展示了三种算法,分别是用于“可有环,权重可正可负”的Bellman-Ford算法,用于“不可有环,权重可正可负”的有向无环图中的单源最短路径算法;用于“可有环,但权重必须非负”的Dijkstra算法。
三种算法思想相似,都用到了一种成为“松弛操作(relaxation)”的技术,松弛操作很简单,射一条边(u,v),权重为w(u,v),v.length为当前已知的v到源节点的最短距离,则当v.length > u.length + w(u,v)时,执行v.length = u.length + w(u,v);v.parent = u;
下面简要介绍一下这三种算法。

Bellman-Ford算法

适用条件:
可有环,权重可正可负

基本步骤:
1.设计好相应的Node、Line、Graph数据结构;
2.输入数据,初始化Graph,并将源节点的length设为0;
3.对每条边进行|G.V|-1次松弛操作;
4.对每条边进行判断是否含有负环;
5.得到答案输出结果。

证明要点:
1.关于对所有边进行|G.V|-1次松弛操作。因为每进行一次松弛操作,会将至少一个新的节点进行松弛操作,而松弛的性质能够使其找到最短路径。所以除去源节点外,|G.V|-1次操作对剩余的所有节点都进行了松弛操作。
2.关于判断是否含有负环的操作。书中解释的比较复杂,我个人是这样理解的:试想一个负环含有A,B两个节点(源节点先到A再到B),总会有A.d+w(A,B)

import java.util.ArrayList;
import java.util.Scanner;
/**
 * 
 * @author FounderWatts
 *
 */
public class Main{
   
    public static void main(String[] args){
        Graph G = new Graph();
        initialize(G);
        boolean hasNoMinusHoop = bellman_ford(G);
        printPath(G);
        System.out.println((hasNoMinusHoop?"没":"") + "有负数环");
    }

    public static void inputData(Graph G){
        Scanner input = new Scanner(System.in);
        int n = input.nextInt();
        ArrayList<Node> nodes = G.getNodes();
        ArrayList<Line> lines = G.getLines();
        input.nextLine();

        for(int i = 0; i < n; ++i){
            nodes.add(new Node(i));
            String line = input.nextLine();
            if(!line.equals("")){
                String[] tempIntStr = line.split(" ");
                for(int j = 0; j < tempIntStr.length; ++j){
                    lines.add(new Line(i,Integer.parseInt(tempIntStr[j]),Integer.parseInt(tempIntStr[++j])));
                }
            }
        }

    }

    public static void initialize(Graph G){
        inputData(G);
        Scanner input = new Scanner(System.in);
        int sourceNodeNo = input.nextInt();
        G.getNodes().get(sourceNodeNo).setLength(0);
    }

    public static void relax(Graph G,Line line){
        Node u = G.getNodes().get(line.getNodeNoStart());
        Node v = G.getNodes().get(line.getNodeNoEnd());
        if(v.getLength() > u.getLength() + line.getWeight()){
            v.setLength(u.getLength() + line.getWeight());
            v.setParent(u);
        }
    }

    public static boolean bellman_ford(Graph G){
        for(int i = 0; i < G.getNodes().size() - 1; ++i){
            for(Line line:G.getLines()){
                relax(G,line);
            }
        }
        for(Line line:G.getLines()){
            Node u = G.getNodes().get(line.getNodeNoStart());
            Node v = G.getNodes().get(line.getNodeNoEnd());
            if(v.getLength() > u.getLength() + line.getWeight())
                return false;
        }
        return true;
    }

    public static void printPath(Graph G){
        for(Node node:G.getNodes()){
            System.out.print("距离为:" + node.getLength() + " 路径为:");
            while(node != null){
                System.out.print("<<" + node.getNo());
                node = node.getParent();
            }
            System.out.println();
        }
    }

}

class Node{
    private int no;
    private int length = 2000000000;
    private Node parent = null;
    public int getNo() {
        return no;
    }
    public void setNo(int no) {
        this.no = no;
    }
    public Node getParent() {
        return parent;
    }
    public void setParent(Node parent) {
        this.parent = parent;
    }
    public int getLength() {
        return length;
    }
    public void setLength(int length) {
        this.length = length;
    }
    public Node(
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值