数据结构 -- 图的最短路径 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];
}
//..........
}
这里是使用的邻接矩阵来表示图,想要使用其他表示方法,自行稍微修改一下便可.下面是实现方法的代码:
/**
* 使用 Dijkstra算法寻找最短路径
* @param first 路径开始的顶点
* @return 返回最后的最短路径
*/
public int[] dijkstraAlgorithm(int first){
if(first < 0 || first >= verticeNum ){
throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
}
setNumberAsInfinitie();
currDist[first] = 0;
List<Integer> toBeChecked = new LinkedList<>();
for(int i = 0; i < verticeNum; i ++){
toBeChecked.add(i);
}
while(!toBeChecked.isEmpty()){
int nowVertice = findMinCurrDistVerticeAndRemoveFormList(toBeChecked);
for(int i = 0; i < verticeNum; i ++){
int nextVertice = -1; //邻接节点
int weight = Integer.MAX_VALUE; //到达邻接节点的权重
if(racs[nowVertice][i] != Integer.MAX_VALUE){ //得到邻接顶点
if(toBeChecked.contains(i)){
nextVertice = i;
weight = racs[nowVertice][i];
}
}
if(nextVertice == -1) {continue;}
if(currDist[nextVertice] > currDist[nowVertice] + weight){
currDist[nextVertice] = currDist[nowVertice] + weight;
}
} }
for(int i = 0; i < currDist.length; i++){
System.out.println("现在顶点 " + verticeInfo[i].toString() + " 距离顶点 " + verticeInfo[first].toString() + " 的最短距离为 " + currDist[i]);
}
return currDist;
}
/**
* 将currDist数组初始化为无穷大
*/
private void setNumberAsInfinitie(){
currDist = new int[verticeNum];
for (int i = 0; i < verticeNum; i++){
currDist[i] = Integer.MAX_VALUE;
}
} /**
* 寻找出当前距离起始顶点路径最短的顶点,并将其从toBeCheck中删除
* @param list
* @return
*/
private int findMinCurrDistVerticeAndRemoveFormList(List<Integer> list){
int num = list.get(0);
int dist = currDist[list.get(0)];
int listIndex = 0;
for(int i = 1; i < list.size(); i ++){
int index = list.get(i);
if(currDist[index] < dist) {
dist = currDist[index];
num = index;
listIndex = i;
}
}
list.remove(listIndex);
return num;
}
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];
}
然后直到在最后一次迭代中,发现所有的边都不符合上面的判断,算法就结束.
实现代码如下:
/**
* 使用Ford的方法寻找最短路径
* @param first 路径开始的顶点
*/
public int[] fordAlgorithm(int first){
if(first < 0 || first >= verticeNum ){
throw new IndexOutOfBoundsException ("should between 0 ~ " + (verticeNum -1));
}
setNumberAsInfinitie();
currDist[first] = 0;
while(true){
boolean hasLessEdge = false; //是否有使currDist更小的边
for(int s = 0 ; s < verticeNum; s ++){
for (int e = 0; e < verticeNum; e ++){
if(racs[s][e] != Integer.MAX_VALUE){
int weight = getWeightPreventOverflow(s,e);
if(currDist[e] > currDist[s] + weight){
hasLessEdge = true;
currDist[e] = currDist[s] + racs[s][e];
}
}
}
}
if(!hasLessEdge) { break; }
}
for(int i = 0; i < currDist.length; i++){
System.out.println("现在顶点 " + verticeInfo[i].toString() + " 距离顶点 " + verticeInfo[first].toString() + " 的最短距离为 " + currDist[i]);
} return currDist;
} /**
* 处理并获得权重,并且使得到的结果在进行路径长度的加减操作时不会出现溢出
* @param start
* @param end
* @return
*/
private int getWeightPreventOverflow(int start, int end){
int weight = 0;
//防止加减法溢出
if(currDist[start] == Integer.MAX_VALUE && racs[start][end] > 0){
weight = 0;
}else if(currDist[start] == Integer.MIN_VALUE && racs[start][end] < 0){
weight = 0;
}else{
weight = racs[start][end];
}
return weight;
}
3.通用型的纠正标记算法
未完待续...
by 自安/henvealf
数据结构 -- 图的最短路径 Java版的更多相关文章
- [从今天开始修炼数据结构]图的最短路径 —— 迪杰斯特拉算法和弗洛伊德算法的详解与Java实现
在网图和非网图中,最短路径的含义不同.非网图中边上没有权值,所谓的最短路径,其实就是两顶点之间经过的边数最少的路径:而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,我们称路径上第 ...
- 数据结构和算法(Java版)快速学习(线性表)
线性表的基本特征: 第一个数据元素没有前驱元素: 最后一个数据元素没有后继元素: 其余每个数据元素只有一个前驱元素和一个后继元素. 线性表按物理存储结构的不同可分为顺序表(顺序存储)和链表(链式存储) ...
- <数据结构>图的最短路径问题
目录 最短路径问题 Dijstra算法:中介点优化 基本步骤 伪代码 在实现过程中的关键问题 代码实现 邻接矩阵版 邻接表版 时间复杂度:O(VlogV+E) 算法存在的问题:存在负权边时会失效 Be ...
- 数据结构和算法(Java版)快速学习(数组Array)
Java数组 在Java中,数组是用来存放同一种数据类型的集合,注意只能存放同一种数据类型. 用类封装数组实现数据结构 数据结构必须具有以下基本功能: ①.如何插入一条新的数据项 ②.如何寻找某一特定 ...
- 数据结构与算法(Java版)_堆
完全二叉树叫做堆. 完全二叉树就是最后一个节点之前不允许有不满的节点,就是不允许有空洞. 可以使用数组来做完全二叉树(堆). 堆分为大顶堆和小顶堆.大顶堆就是根节点上的数字是最大的,小顶堆就是根节点上 ...
- 数据结构和算法(Java版)快速学习(栈与队列)
栈是仅允许在表尾进行插入和删除操作的线性表.我们把允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom).栈是一种后进先出(Last In First Out)的线性表,简称(LIFO ...
- 数据结构和算法(Java版)快速学习(交换、选择、插入排序)
基本排序算法:交换.选择.插入排序 常用的交换排序又称之为:冒泡排序 一般河水中的冒泡,水底刚冒出来的时候是比较小的,随着慢慢向水面浮起会逐渐增大,冒泡排序由此物理规律得来. 冒泡算法的运作规律如下: ...
- <数据结构>图的最小生成树
目录 最小生成树问题 Prim算法:点贪心 基本思想:类Dijstra 伪代码 代码实现 复杂度分析:O(VlogV + E) kruskal算法:边贪心 基本思想: 充分利用MST性质 伪代码 代码 ...
- 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径1
import java.util.ArrayList; import java.util.List; // 模块E public class AdjMatrixGraph<E> { pro ...
随机推荐
- [Golang]实习最后一天小纪念+并发爬虫小练习
今天是我在公司实习的最后一天,一个月的时间真的是太短暂了,我非常享受在公司工作的这一个月,在这里Leader和同事们对我的帮助极大地促进了我技术水平的进步和自信心的提升,我发自内心地感谢白山云科技给我 ...
- 【Todo】Java Queue Stack Vector ArrayList
Java集合框架里存在Queue这个接口,之后有不同类型的队列的实现. 有Stack这个类实现堆栈,其实这个类是通过继承Vector的方式来实现的, Vector和ArrayList的实现方式差不多, ...
- Codeforces Round #272 (Div. 2) D. Dreamoon and Sets (思维 数学 规律)
题目链接 题意: 1-m中,四个数凑成一组,满足任意2个数的gcd=k,求一个最小的m使得凑成n组解.并输出 分析: 直接粘一下两个很有意思的分析.. 分析1: 那我们就弄成每组数字都互质,然后全体乘 ...
- Android EditText截获与监听输入事件
Android EditText截获与监听输入事件共有2种方法: 1.第一种方法:使用setOnKeyListener(),不过这种方式只能监听硬键盘事件. edittext.setOnKeyLi ...
- Miller_Rabin (米勒-拉宾) 素性测试
之前一直对于这个神奇的素性判定方法感到痴迷而又没有时间去了解.借着学习<信息安全数学基础>将素性这一判定方法学习一遍. 首先证明一下费马小定理. 若p为素数,且gcd(a, p)=1, 则 ...
- C#处理文件流的转换
//----引入必要的命名空间 using System.IO; using System.Drawing.Imaging; //----代码部分----// private byte[] photo ...
- 单点登录系统构建之一——基础知识(Kerberous/SAML)
http://web.mit.edu/kerberos/ Kerberos Kerberous是一个网络身份验证协议,它被设计为客户端/服务器提供基于密钥的强加密机制.该协议最初由MIT实现并被广泛商 ...
- Windows 桌面软件:不绑定bing搜索的缤纷桌面
bing:世界上最好的壁纸提供商 ^.^一直垂涎着Bing的壁纸,总是想找机会来一番邂逅. 之前使用bing自家的缤纷桌面.这个软件缺点就是和bing搜索绑定太厉害,放在桌面上感觉那个黑色的条框很碍 ...
- Windows 7 32位上硬盘安装linux[ubuntu13.04] 双系统
本内容介绍如何在window7上安装ubuntu双系统 一.准备工具 1. EasyBCD : 用来制作引导菜单选项 2.Wingrub : 用来确定磁盘文件Linux表示法位置 3.分区助手 :用来 ...
- MVC简捷调用EasyUI的datagrid
一直想在项目中使用EasyUi的datagrid,但种种原因,没有实现. 这两天在开发一个项目中,愿望终于得以实现. 先看效果: 实现步骤是这样的: 1,在页面中画dataGrid,具体代码如下: & ...