Java邻接表表示加权有向图,附dijkstra最短路径算法
从A到B,有多条路线,要找出最短路线,应该用哪种数据结构来存储这些数据。
这不是显然的考查图论的相关知识了么,
1.图的两种表示方式:
邻接矩阵:二维数组搞定。
邻接表:Map<Vertext,List<Edge>>搞定。
其中邻接矩阵适用于稠密图,即图上的任意两点之间均(差不多都)存在一条边。
而A到B之间的路线,显然是稀疏图,果断的选用邻接表。
2.加权有向图最短路径问题,典型的dijkstra最短路径算法。
说干就干,翻翻《数据结构与算法》,自己用Java大概实现了一下,具体代码如下:
实现思路:
1,定义一个类:有向图类:Graph。
有向图类的子类:节点类:Vertex,边类:Vertex。
节点类:保存节点名称,上一个节点,长度等属性。
边节点:保存每条边的两边的节点,通过边找到对应的另一条节点。
2,该类有两个属性:
1,List<Vertex> vertexList:保存图的顶点集合,便于遍历顶点的时候查找对应集合。
2,Map<Vertex, List<Edge>> ver_edgeList_map:图的每个顶点对应的有向边。
3,为了能够记录最短路径,需要为每个节点定义一个属性:父节点,表示父节点到该点的距离最短。
3,每个节点有多个属性:
String name; //节点名字
boolean known; //此节点之前是否已知,如果未知的话,则需要初始化距离adjuDist和parent属性
int adjuDist; //保存从开始节点到此节点距离
Vertex parent; //当前从初始节点到此节点的最短路径下,的父节点。
4,从起点节点开始查找。
比较规则:从A节点开始比较,对其指向的B节点进行初始化和比较:
如果B节点未被初始化,先设置该B节点的父节点为A节点,距离为边长加上A节点的adjuDist。
如果已经初始化完了,则重新比较:
如果A节点加边长小于B节点的adjuDist,则证明A节点到B节点的距离最短,设置A节点为B节点父节点,并且长度修改为A节点的adjuDist加上边长。
否则不做操作。
5,等所有的节点初始化完了,从终止节点开始,通过终止节点的父节点找到上一个节点,输出节点的路径。
代码如下:
package 笔试题; import java.util.LinkedList;
import java.util.List;
import java.util.Map; public class Graph{ private List<Vertex> vertexList; //图的顶点集
private Map<Vertex, List<Edge>> ver_edgeList_map; //图的每个顶点对应的有向边 public Graph(List<Vertex> vertexList, Map<Vertex, List<Edge>> ver_edgeList_map) {
super();
this.vertexList = vertexList;
this.ver_edgeList_map = ver_edgeList_map;
} public List<Vertex> getVertexList() {
return vertexList;
} public void setVertexList(List<Vertex> vertexList) {
this.vertexList = vertexList;
} public Map<Vertex, List<Edge>> getVer_edgeList_map() {
return ver_edgeList_map;
} public void setVer_edgeList_map(Map<Vertex, List<Edge>> ver_edgeList_map) {
this.ver_edgeList_map = ver_edgeList_map;
} static class Edge{
private Vertex startVertex; //此有向边的起始点
private Vertex endVertex; //此有向边的终点
private int weight; //此有向边的权值 public Edge(Vertex startVertex, Vertex endVertex, int weight) {
super();
this.startVertex = startVertex;
this.endVertex = endVertex;
this.weight = weight;
} public Edge()
{} public Vertex getStartVertex() {
return startVertex;
}
public void setStartVertex(Vertex startVertex) {
this.startVertex = startVertex;
}
public Vertex getEndVertex() {
return endVertex;
}
public void setEndVertex(Vertex endVertex) {
this.endVertex = endVertex;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
} static class Vertex {
private final static int infinite_dis = Integer.MAX_VALUE; private String name; //节点名字
private boolean known; //此节点之前是否已知
private int adjuDist; //此节点距离
private Vertex parent; //当前从初始节点到此节点的最短路径下,的父节点。 public Vertex()
{
this.known = false;
this.adjuDist = infinite_dis;
this.parent = null;
} public Vertex(String name)
{
this.known = false;
this.adjuDist = infinite_dis;
this.parent = null;
this.name = name;
} public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isKnown() {
return known;
}
public void setKnown(boolean known) {
this.known = known;
}
public int getAdjuDist() {
return adjuDist;
}
public void setAdjuDist(int adjuDist) {
this.adjuDist = adjuDist;
} public Vertex getParent() {
return parent;
} public void setParent(Vertex parent) {
this.parent = parent;
} /**
* 重新Object父类的equals方法
*/
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Vertex)) {
throw new ClassCastException("an object to compare with a Vertext must be Vertex");
} if (this.name==null) {
throw new NullPointerException("name of Vertex to be compared cannot be null");
} return this.name.equals(obj);
}
} public void setRoot(Vertex v)
{
v.setParent(null);
v.setAdjuDist(0);
} /**
*
* @param startIndex dijkstra遍历的起点节点下标
* @param destIndex dijkstra遍历的终点节点下标
*/
public void dijkstraTravasal(int startIndex,int destIndex)
{
Vertex start = vertexList.get(startIndex);
Vertex dest = vertexList.get(destIndex);
String path = "["+dest.getName()+"]"; setRoot(start);
updateChildren(vertexList.get(startIndex)); int shortest_length = dest.getAdjuDist(); while((dest.getParent()!=null)&&(!dest.equals(start)))
{
path = "["+dest.getParent().getName()+"] --> "+path;
dest = dest.getParent();
} System.out.println("["+vertexList.get(startIndex).getName() +"] to ["+
vertexList.get(destIndex).getName()+"] dijkstra shortest path :: "+path);
System.out.println("shortest length::"+shortest_length);
} /**
* 从初始节点开始递归更新邻接表
* @param v
*/
private void updateChildren(Vertex v)
{
if (v==null) {
return;
} if (ver_edgeList_map.get(v)==null||ver_edgeList_map.get(v).size()==0) {
return;
}
//用来保存每个可达的节点
List<Vertex> childrenList = new LinkedList<Graph.Vertex>();
for(Edge e:ver_edgeList_map.get(v))
{
Vertex childVertex = e.getEndVertex(); //如果子节点之前未知,则进行初始化,
//把当前边的开始点默认为子节点的父节点,长度默认为边长加边的起始节点的长度,并修改该点为已经添加过,表示不用初始化
if(!childVertex.isKnown())
{
childVertex.setKnown(true);
childVertex.setAdjuDist(v.getAdjuDist()+e.getWeight());
childVertex.setParent(v);
childrenList.add(childVertex);
} //此时该子节点的父节点和之前到该节点的最小长度已经知道了,则比较该边起始节点到该点的距离是否小于子节点的长度,
//只有小于的情况下,才更新该点为该子节点父节点,并且更新长度。
int nowDist = v.getAdjuDist()+e.getWeight();
if(nowDist>=childVertex.getAdjuDist())
{
continue;
}
else {
childVertex.setAdjuDist(nowDist);
childVertex.setParent(v);
}
} //更新每一个子节点
for(Vertex vc:childrenList)
{
updateChildren(vc);
}
} }
测试代码:
package 笔试题; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map; import 笔试题.Graph.Edge;
import 笔试题.Graph.Vertex; /**
* 测试用main方法
* @author wuhui.wwh
*
*/
public class TestGraph {
public static void main(String[] args) {
Vertex v1= new Vertex("v1");
Vertex v2= new Vertex("v2");
Vertex v3= new Vertex("v3");
Vertex v4= new Vertex("v4");
Vertex v5= new Vertex("v5");
Vertex v6= new Vertex("v6");
Vertex v7= new Vertex("v7");
Vertex v8= new Vertex("v8"); List<Vertex> verList = new LinkedList<Graph.Vertex>();
verList.add(v1);
verList.add(v2);
verList.add(v3);
verList.add(v4);
verList.add(v5);
verList.add(v6);
verList.add(v7);
verList.add(v8); Map<Vertex, List<Edge>> vertex_edgeList_map = new HashMap<Graph.Vertex, List<Edge>>(); List<Edge> v1List = new LinkedList<Graph.Edge>();
v1List.add(new Edge(v1,v2,6));
v1List.add(new Edge(v1,v4,1));
v1List.add(new Edge(v1,v4,1)); List<Edge> v2List = new LinkedList<Graph.Edge>();
v2List.add(new Edge(v2,v3,43));
v2List.add(new Edge(v2,v4,11));
v2List.add(new Edge(v2,v5,6)); List<Edge> v3List = new LinkedList<Graph.Edge>();
v3List.add(new Edge(v3,v8,8)); List<Edge> v4List = new LinkedList<Graph.Edge>();
v4List.add(new Edge(v4,v3,15));
v4List.add(new Edge(v4,v5,12)); List<Edge> v5List = new LinkedList<Graph.Edge>();
v5List.add(new Edge(v5,v3,38));
v5List.add(new Edge(v5,v8,13));
v5List.add(new Edge(v5,v7,24)); List<Edge> v6List = new LinkedList<Graph.Edge>();
v6List.add(new Edge(v6,v5,1));
v6List.add(new Edge(v6,v7,12)); List<Edge> v7List = new LinkedList<Graph.Edge>();
v7List.add(new Edge(v7,v8,20)); vertex_edgeList_map.put(v1, v1List);
vertex_edgeList_map.put(v2, v2List);
vertex_edgeList_map.put(v3, v3List);
vertex_edgeList_map.put(v4, v4List);
vertex_edgeList_map.put(v5, v5List);
vertex_edgeList_map.put(v6, v6List);
vertex_edgeList_map.put(v7, v7List); Graph g = new Graph(verList, vertex_edgeList_map);
g.dijkstraTravasal(0, 7);
}
}
运行结果:
[v1] to [v8] dijkstra shortest path :: [v1] --> [v2] --> [v5] --> [v8]
shortest length::25
Java邻接表表示加权有向图,附dijkstra最短路径算法的更多相关文章
- 数据结构(c++)(第二版) Dijkstra最短路径算法 教学示范代码出现重大问题!
前言 去年在数据结构(c++)的Dijkstra教学算法案例中,发现了一个 bug 导致算法不能正常的运行,出错代码只是4行的for循环迭代代码. 看到那里就觉得有问题,但书中只给了关键代码的部分,其 ...
- 练习 Dijkstra 最短路径算法。
练习 Dijkstra 最短路径算法. #coding: utf-8 # Author: woodfox, Oct 14, 2014 # http://en.wikipedia.org/wiki/Di ...
- Dijkstra 最短路径算法 秒懂详解
想必大家一定会Floyd了吧,Floyd只要暴力的三个for就可以出来,代码好背,也好理解,但缺点就是时间复杂度高是O(n³). 于是今天就给大家带来一种时间复杂度是O(n²),的算法:Dijkstr ...
- 7-9 旅游规划(25 分)(Dijkstra最短路径算法)
有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路费.现在需要你写一个程序,帮助前来咨询的游客找一条出发地和目的地之间的最短路径.如果有若干条路径都是最短的,那么需要输出最便 ...
- Python 图_系列之纵横对比 Bellman-Ford 和 Dijkstra 最短路径算法
1. 前言 因无向.无加权图的任意顶点之间的最短路径由顶点之间的边数决定,可以直接使用原始定义的广度优先搜索算法查找. 但是,无论是有向.还是无向,只要是加权图,最短路径长度的定义是:起点到终点之间所 ...
- Dijkstra最短路径算法[贪心]
Dijkstra算法的标记和结构与prim算法的用法十分相似.它们两者都会从余下顶点的优先队列中选择下一个顶点来构造一颗扩展树.但千万不要把它们混淆了.它们解决的是不同的问题,因此,所操作的优先级也是 ...
- 一篇文章讲透Dijkstra最短路径算法
Dijkstra是典型最短路径算法,计算一个起始节点到路径中其他所有节点的最短路径的算法和思想.在一些专业课程中如数据结构,图论,运筹学等都有介绍.其思想是一种基础的求最短路径的算法,通过基础思想的变 ...
- Dijkstra最短路径算法
#include <iostream> #include <vector> #include <cstring> #include <cstdio> # ...
- Dijkstra最短路径算法实例
#include <stdio.h>#include <stdlib.h>/* Dijkstra算法 */#define VNUM 5#define MV 65536int P ...
随机推荐
- fortran中提取字符串中可见字符的索引
fortran中常常需要提取字符串中可见字符的索引,下面是个小例子: !============================================================= su ...
- PHP一句话木马研究
最近在研究PHP一句话后门,查阅了很多大佬的博客,并从中衍生出了一些可用的方法. 现总结如下: 方案一:回调函数 回调函数:Callback (即call then back 被主函数调用运算后会返回 ...
- Go语言之进阶篇http服务器获取客户端的一些信息
1.http服务器获取客户端的一些信息 示例: package main import ( "fmt" "net/http" ) //w, 给客户端回复数据 / ...
- go语言之进阶篇 channel介绍
1.channel介绍 和map类似,channel也一个对应make创建的底层数据结构的引用. 当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者何被 ...
- windows包管理器chocolatey
1.安装chocolatey打开cmd.exe执行@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((New-Obj ...
- java语言特性之一
package java.util.Collections; public static int indexOfSubList(List<?> source, List<?> ...
- Rust 的安装和使用举例
一.环境 二.安装 $curl -sSf https://static.rust-lang.org/rustup.sh | sh Welcome to Rust. This script will d ...
- Mycat读写分离、主从切换学习(转)
http://blog.csdn.net/zhanglei_16/article/details/50707487 Mycat读写分离.主从切换学习问题一:分表.分库的优缺点,以及分表无法成为主流分表 ...
- jQuery 1
<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="utf-8& ...
- 解决 IE6 背景缓存
解决 IE6 背景缓存 <!--[if IE 6]><script type="text/javascript">document.execCommand( ...