数据结构 -- 图的最短路径 Java版

  作者版权所有,转载请注明出处,多谢.http://www.cnblogs.com/Henvealf/p/5574455.html

  上一篇介绍了有关图的表示和遍历实现.数据结构 -- 简单图的实现与遍历 (Java)现在就来看看关于求图的最短路径的问题:

  注意:本人学习图的时候看的书是:

    <<数据结构与算法 Java语言版>> (美)Adam Drozdek/著 周翔/译 机械工业出版社出版

  由于要仔细讲解内容过多并且本人水平有限,推荐大家找出这本书来看,本篇文章主要是对其中Dijkstra 算法,Ford 算法 ,通用型的纠正标记算法这三个伪代码的实现.开始页数为P284.

1.Dijkstra 算法

  首先来看一下 Dijkstra 算法,它不能够处理权值为负的图.本算法的主要步骤:

  

  1.找出距离起始顶点距离最短的顶点,这里设为顶点nowVertice.

  2.遍历所有与顶点nowVertice相邻的顶点nextVertice.如果发现选择nowVertice到达nextVertice的路径后,nextVertice距离起始顶点的距离比当前的距离小.便更新新的距离.如下:

    if(currDist[nextVertice] > currDist[nowVertice] + weight) {            //weight为从nowVertice到nextVertice说需要的权重
                    currDist[nextVertice] = currDist[nowVertice] + weight;
     }

    currDist是一个全局数组,currDist[i]意思就是当前起始顶点到顶点i的距离.

  3.将nowVertice从图中删除.

  4.重复步骤1,直到所有的顶点都被删除完.

  补充,在实现的时候,上面说的删除并不是真的直接从图中把某一顶点删除,这里会使用一个集合来存储所有的顶点,对该集合中的顶点进行删除动作,集合如下.

List<Integer> toBeChecked = new LinkedList<>();

  

  和上一篇一样,这里使用一个名为Graph的类来封装查找最短路径的相关内容:

/**
 * 使用邻接矩阵实现图<p>
 * 深度优先遍历与广度优先遍历<p>
 * 求最短路径:<p>
 *      1. Dijkstra 算法 <p>
 *      2. Ford 算法 <p>
 *      3. 通用型的纠正标记算法<p>
 * Created by Henvealf on 16-5-22.
 */
public class Graph<T> {
    private int[][] racs;       //邻接矩阵
    private T[] verticeInfo;   //各个点所携带的信息.

    private int verticeNum;             //顶点的数目,
    private int[] visitedCount;         //记录访问
    private int[] currDist;             //最短路径算法中用来记录每个顶点距离起始顶点路径的长度.

    public Graph(int[][] racs, T[] verticeInfo){
        if(racs.length != racs[0].length){
            throw new IllegalArgumentException("racs is not a adjacency matrix!");
        }
        if(racs.length != verticeInfo.length ){
            throw new IllegalArgumentException ("Argument of 2 verticeInfo‘s length is error!");
        }
        this.racs = racs;
        this.verticeInfo = verticeInfo;
        verticeNum = racs.length;
        visitedCount = new int[verticeNum];
    }
    //..........
}

  这里是使用的邻接矩阵来表示图,想要使用其他表示方法,自行稍微修改一下便可.下面是实现方法的代码:

 1 /**
 2      * 使用 Dijkstra算法寻找最短路径
 3      * @param first 路径开始的顶点
 4      * @return 返回最后的最短路径
 5      */
 6     public int[] dijkstraAlgorithm(int first){
 7         if(first < 0 || first >= verticeNum ){
 8             throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
 9         }
10         setNumberAsInfinitie();
11         currDist[first] = 0;
12         List<Integer> toBeChecked = new LinkedList<>();
13         for(int i = 0; i < verticeNum; i ++){
14             toBeChecked.add(i);
15         }
16         while(!toBeChecked.isEmpty()){
17             int nowVertice = findMinCurrDistVerticeAndRemoveFormList(toBeChecked);
18             for(int i = 0; i < verticeNum; i ++){
19                 int nextVertice = -1;                       //邻接节点
20                 int weight = Integer.MAX_VALUE;             //到达邻接节点的权重
21                 if(racs[nowVertice][i] != Integer.MAX_VALUE){   //得到邻接顶点
22                     if(toBeChecked.contains(i)){
23                         nextVertice = i;
24                         weight = racs[nowVertice][i];
25                     }
26                 }
27                 if(nextVertice == -1) {continue;}
28                 if(currDist[nextVertice] > currDist[nowVertice] + weight){
29                     currDist[nextVertice] = currDist[nowVertice] + weight;
30                 }
31             }
32
33         }
34         for(int i = 0; i < currDist.length; i++){
35             System.out.println("现在顶点 " + verticeInfo[i].toString() + " 距离顶点 " + verticeInfo[first].toString()  + " 的最短距离为 " + currDist[i]);
36         }
37         return currDist;
38     }
39   /**
40      * 将currDist数组初始化为无穷大
41      */
42     private void setNumberAsInfinitie(){
43         currDist = new int[verticeNum];
44         for (int i = 0; i < verticeNum; i++){
45             currDist[i] = Integer.MAX_VALUE;
46         }
47     }
48
49     /**
50      * 寻找出当前距离起始顶点路径最短的顶点,并将其从toBeCheck中删除
51      * @param list
52      * @return
53      */
54     private int findMinCurrDistVerticeAndRemoveFormList(List<Integer> list){
55         int num = list.get(0);
56         int dist = currDist[list.get(0)];
57         int listIndex = 0;
58         for(int i = 1; i < list.size(); i ++){
59             int index = list.get(i);
60             if(currDist[index] < dist) {
61                 dist = currDist[index];
62                 num = index;
63                 listIndex = i;
64             }
65         }
66         list.remove(listIndex);
67         return num;
68     }

2.Ford 算法 

  上面提到Dijkstra算法不能处理有负权值的情况,所以自然就有替代方法:Ford方法.

  Ford算法并不会像Dijkstra算法一样去删除顶点,他时按照一定的顺序,来对每个边进行遍历并更新设置最短距离.

  比如有一个异常简单的图:

    a-->b-->c-->d

  Ford算法要求我们指定边的遍历顺序,让每条边都能够被走过一次.比如这里我选择的顺序为:b-->c,  a-->b, c-->d.

  算法就会根据指定的该顺序,把图中所有的边都访问一次,每访问完一遍就是一次迭代.在访问过程中,和Dijkstra算法相似,回进行如下判断和更新.

if(currDist[now] > currDist[next] + weight){
         currDist[next] = currDist[now] + racs[now][next];
}

  然后直到在最后一次迭代中,发现所有的边都不符合上面的判断,算法就结束.

  实现代码如下:

 1 /**
 2      * 使用Ford的方法寻找最短路径
 3      * @param first 路径开始的顶点
 4      */
 5     public int[] fordAlgorithm(int first){
 6         if(first < 0 || first >= verticeNum ){
 7             throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
 8         }
 9         setNumberAsInfinitie();
10         currDist[first] = 0;
11         while(true){
12             boolean hasLessEdge = false;            //是否有使currDist更小的边
13             for(int s = 0 ; s < verticeNum; s ++){
14                 for (int e = 0; e < verticeNum; e ++){
15                     if(racs[s][e] != Integer.MAX_VALUE){
16                         int weight = getWeightPreventOverflow(s,e);
17                         if(currDist[e] > currDist[s] + weight){
18                             hasLessEdge = true;
19                             currDist[e] = currDist[s] + racs[s][e];
20                         }
21                     }
22                 }
23             }
24             if(!hasLessEdge) { break; }
25         }
26         for(int i = 0; i < currDist.length; i++){
27             System.out.println("现在顶点 " + verticeInfo[i].toString() + " 距离顶点 " + verticeInfo[first].toString()  + " 的最短距离为 " + currDist[i]);
28         }
29
30         return currDist;
31     }
32
33     /**
34      * 处理并获得权重,并且使得到的结果在进行路径长度的加减操作时不会出现溢出
35      * @param start
36      * @param end
37      * @return
38      */
39     private int getWeightPreventOverflow(int start, int end){
40         int weight = 0;
41         //防止加减法溢出
42         if(currDist[start] == Integer.MAX_VALUE && racs[start][end] > 0){
43             weight = 0;
44         }else if(currDist[start] == Integer.MIN_VALUE && racs[start][end] < 0){
45             weight = 0;
46         }else{
47             weight = racs[start][end];
48         }
49         return weight;
50     }

3.通用型的纠正标记算法

未完待续...

by 自安/henvealf

时间: 2024-10-12 17:12:39

数据结构 -- 图的最短路径 Java版的相关文章

[从今天开始修炼数据结构]图的最短路径 —— 迪杰斯特拉算法和弗洛伊德算法的详解与Java实现

在网图和非网图中,最短路径的含义不同.非网图中边上没有权值,所谓的最短路径,其实就是两顶点之间经过的边数最少的路径:而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,我们称路径上第一个顶点是源点,最后一个顶点是终点. 我们讲解两种求最短路径的算法.第一种,从某个源点到其余各顶点的最短路径问题. 1,迪杰斯特拉(Dijkstra)算法 迪杰斯特拉算法是一个按路径长度递增的次序产生最短路径的算法,每次找到一个距离V0最短的点,不断将这个点的邻接点加入判断,更新新加入的点到V0的距

数据结构与算法(java版)摘要一

1.为了得到两个完全一样的数组(1)循环遍历(2)system类的arraycopy方法. 2.当数组作为方法的参数传递时,传递的为数组的引用,对数组的操作会影响原来的数组. 3.this 关键字(1)隐式参数引用(2)调用类中的其他构造方法 4.super关键字(1)调用父类方法(2)调用父类构造方法 5.java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例.l例子

数据结构与算法(Java版)_堆

完全二叉树叫做堆. 完全二叉树就是最后一个节点之前不允许有不满的节点,就是不允许有空洞. 可以使用数组来做完全二叉树(堆). 堆分为大顶堆和小顶堆.大顶堆就是根节点上的数字是最大的,小顶堆就是根节点上的数字是最小的堆. 在堆里面的操作包括两种:插入新的节点和删除根节点. 插入新节点的操作时向上渗透.删除根节点的操作是向下渗透. 插入新节点时,把新的节点插入到最后一个位置,然后慢慢向上渗透(和父辈交换).删除根节点时,把最后一个节点放到根节点上,然后再慢慢向下渗透(和子代交换). 下面使用Java

数据结构--图 的JAVA实现(上)

1,摘要: 本系列文章主要学习如何使用JAVA语言以邻接表的方式实现了数据结构---图(Graph),这是第一篇文章,学习如何用JAVA来表示图的顶点.从数据的表示方法来说,有二种表示图的方式:一种是邻接矩阵,其实是一个二维数组:一种是邻接表,其实是一个顶点表,每个顶点又拥有一个边列表.下图是图的邻接表表示. 从图中可以看出,图的实现需要能够表示顶点表,能够表示边表.邻接表指是的哪部分呢?每个顶点都有一个邻接表,一个指定顶点的邻接表中,起始顶点表示边的起点,其他顶点表示边的终点.这样,就可以用邻

数据结构--图 的JAVA实现(下)

在上一篇文章中记录了如何实现图的邻接表.本文借助上一篇文章实现的邻接表来表示一个有向无环图. 1,概述 图的实现与邻接表的实现最大的不同就是,图的实现需要定义一个数据结构来存储所有的顶点以及能够对图进行什么操作,而邻接表的实现重点关注的图中顶点的实现,即怎么定义JAVA类来表示顶点,以及能够对顶点进行什么操作. 为了存储图中所有的顶点,定义了一个Map<key, value>,实际实现为LinkedHashMap<T, VertexInterface<T>>,key 为

Java甘特图控件swing版免费下载地址 &nbsp; &nbsp; &nbsp;

FlexGantt 控件是现在Java 平台下最先进的甘特图解决方案,使用一个很高的抽象层次,能适用于多种不同的域,例如 ERP 系统.生产计划和日程安排.制造流程系统或项目公文管理程序等.这些使得 FlexGantt 能从其他有局限性的项目计划(资源.人.任务)甘特图库中脱颖而出. 具体功能: 原文来自http://www.51diaodu.com/pdt/2966 模型视图控制器:FlexGantt 遵从与 Swing 相同 MVC 方法.许多不同的模型用于各种各样的用途.一个日历模型跟踪假

Java版数据结构插入数据时遇到的结点为空的问题

在演示Java版数据结构与算法教材中的头插法代码时遇到了空结点问题 . 先上代码. 链表类 import java.util.Scanner; public class ListLinked<T> { ListLinkedNode<Integer> head=new ListLinkedNode<Integer>();//声明头结点 //添加结点 public void addFromHead(int e){ ListLinkedNode<Integer>

玩转算法系列--图论精讲 面试升职必备(Java版)

第1章 和bobo老师一起,玩转图论算法欢迎大家来到我的新课程:<玩转图论算法>.在这个课程中,我们将一起完整学习图论领域的经典算法,培养大家的图论建模能力.通过这个课程的学习,你将能够真正地,玩转图论算法:) 第2章 图的基本表示千里之行,驶于足下.解决任何有一个图论算法问题,首先需要用基本的数据结构来表示图.在这一章,我们就将探索图的基本表示问题,学习邻接矩阵和邻接表,进而,也让同学们熟悉这个课程的整体代码风格. 第3章 图的深度优先遍历任何一种数据结构,都需要进行遍历.图也不例外.通过深

带权图的最短路径算法(Dijkstra)实现

一,介绍 本文实现带权图的最短路径算法.给定图中一个顶点,求解该顶点到图中所有其他顶点的最短路径 以及 最短路径的长度.在决定写这篇文章之前,在网上找了很多关于Dijkstra算法实现,但大部分是不带权的.不带权的Dijkstra算法要简单得多(可参考我的另一篇:无向图的最短路径算法JAVA实现):而对于带权的Dijkstra算法,最关键的是如何“更新邻接点的权值”.本文采用最小堆主辅助,以重新构造堆的方式实现更新邻接点权值. 对于图而言,存在有向图和无向图.本算法只需要修改一行代码,即可同时实