最短路径分为点到点最短路径
和源点到其他点的最短路径
问题,下面给出广度优先BFS算法
的实现。
这里采用迷宫问题来举例。求从起点到终点的最短路径,采用广度优先的顺序,首先将与源点邻接的顶点的路径求出,然后再依次求解图中其他顶点的最短路径。
由于顶点的最短路径的求解顺序 是一个广度优先的顺序,因此需要一个辅助队列。具体步骤如下:
①从起点开始,先将其加入队列,设置距离为0;
②从队列首端取出位置,将从这个位置能够到达的位置加入队列,并且让这些位置的距离为上一个位置的距离加上1;
③循环2直到将终点添加到队列中,这说明我们已经找到了路径;
在这个过程中,每次处理的位置所对应的距离是严格递增的,因此一旦找到终点,当时的距离就是最短距离。
由此可见,迷宫问题是一个无向无权图。
import java.awt.Point;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;
public class Maze {
static int M, N, dx, dy, gx, gy;
static char[][] a;
static int[][] deep;
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
System.out.println("请输入迷宫的行数:");
M = s.nextInt();
System.out.println("请输入迷宫的列数:");
N = s.nextInt();
a = new char[M][N];
deep = new int[M][N];
System.out.println("请输入迷宫矩阵(起点为'm',终点为'e',墙壁为'1',可行路径为'0'):");
for (int i = 0; i < M; i++) {
a[i] = s.next().toCharArray();
}
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++) {
if (a[i][j] == 'm') {
dx = i;
dy = j;
}
if (a[i][j] == 'e') {
gx = i;
gy = j;
}
}
int len = bfs();
System.out.println("从起点到终点的最短路径为:" + len);
}
private static int bfs() {
Queue q = new LinkedList();
int[] tx = { -1, 1, 0, 0 };
int[] ty = { 0, 0, 1, -1 };
q.offer(new Point(dx, dy));
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
deep[i][j] = -1;
deep[dx][dy] = 0;
while (q.size() > 0) {
Point p = q.poll();
if (p.x == gx && p.y == gy)
break;
for (int i = 0; i < 4; i++) {
int x = p.x + tx[i];
int y = p.y + ty[i];
if (x >= 0 && x < M && y >= 0 && y < N && a[x][y] != '1'
&& deep[x][y] == -1) {
deep[x][y] = deep[p.x][p.y] + 1;
q.offer(new Point(x, y));
}
}
}
return deep[gx][gy];
}
}
测试数据为10*10矩阵;
矩阵为:
1111111111
1m01000101
1001000101
1000011001
1011100001
1000100001
1010001001
1011101101
11000000e1
1111111111
源点的最短路径距离为0,从源点开始,采用广度优先的顺序,首先将与源点邻接的顶点的路径求出,然后再依次求解图中其他顶点的最短路径。
由于顶点的最短路径的求解顺序、是一个广度优先的顺序,因此需要一个辅助队列。初始时,将源点的最短路径距离设置为0,将源点入队列。
然后,在一个while循环中,从队列中弹出顶点,遍历该顶点的邻接点,若该邻接点的距离未被更新过(表示该邻接点未被访问过),更新邻接点的最短路径距离为该顶点的距离加上1,并将所有的邻接点入队列。
这里采用的图为有向无权图。
//边类:
class Edge {
String to;//边指向的节点
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
Edge nextEdge;//具有同一起点的下一条边
public Edge(String to) {
this.to = to;
this.nextEdge = null;
}
}
//点类:
public class Vertex {
String from;// 边的起点
int pathLength = 0;//路径长度
boolean isVisted = false;
int indegree;// 每个顶点的入度
Edge first;// 以from为起点的第一条边
public boolean getIsVisted() {
return isVisted;
}
public void setIsVisted(boolean isVisted) {
this.isVisted = isVisted;
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public Vertex(String from) {
this.from = from;
first = null;
this.indegree = 0;
}
public int getPathlength() {
return pathLength;
}
public void setPathlength(int counter) {
pathLength = counter;
}
}
//图类:
import java.util.ArrayList;
import java.util.Scanner;
public class Graph {
public Vertex[] v;
public Edge[] e;
public int edgeNumber;
public int vertexNumber;
// 根据输入建立一个有向图
public void buildGraph() {
Scanner s = new Scanner(System.in);
System.out.println("请输入有向图的顶点数:");
vertexNumber = s.nextInt();
System.out.println("请输入有向图的边数:");
edgeNumber = s.nextInt();
// 建立顶点数组
v = new Vertex[vertexNumber];
e = new Edge[edgeNumber];
System.out.println("请输入顶点的名称:");
for (int i = 0; i < vertexNumber; i++) {
String name = s.next();
v[i] = new Vertex(name);
}
for (int i = 0; i < edgeNumber; i++) {
System.out.print("输入第" + (i + 1) + "条有向边的端点A:");
String startVertex = s.next();
System.out.print("输入第" + (i + 1) + "条有向边的端点B:");
String endVertex = s.next();
// 找到起止点的vertex索引值
int vBeginIndex = findvIndex(startVertex);
int vEndIndex = findvIndex(endVertex);
e[i] = new Edge(endVertex);// 由终点建立到该终点的边
v[vEndIndex].indegree++;// 相应Vertex的入度+1
e[i].nextEdge = v[vBeginIndex].first;// 将该边的下一条边连接到以startVertex的所有边
v[vBeginIndex].first = e[i];// 将e作为v[startVertex]的第一条边
}
}
public int getLength() {
return v.length;
}
// 返回一个包含相邻节点的ArrayList
public ArrayList getAdjacentVertex(Vertex ver) {
int index;
ArrayList al = new ArrayList();
// 找到指向ver的相邻节点
for (int j = 0; j < v.length; j++) {
if (v[j].first != null)
for (Edge e = v[j].first; e != null; e = e.nextEdge)
if (e.to.equals(ver.from)) {
al.add(v[j]);
}
}
index = findvIndex(ver.from);
// 找到以ver为起点指向的相邻节点
for (Edge e = v[index].first; e != null; e = e.nextEdge) {
al.add(v[findvIndex(e.to)]);
}
return al;
}
public int findvIndex(String s) {
int vIndex = -1;
for (int j = 0; j < v.length; j++) {
if (v[j].from.equals(s))
vIndex = j;
}
return vIndex;
}
}
//主类:
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;
public class BFS {
Graph g;
public BFS() {
g = new Graph();
g.buildGraph();
}
public static void main(String[] args) {
BFS s = new BFS();
System.out.print("输入路径的起点:");
Scanner input = new Scanner(System.in);
String first = input.next();
s.shortestPath(first);
}
public void shortestPath(String first) {
LinkedList queue = new LinkedList();
Vertex firstVertex = g.v[g.findvIndex(first)];
firstVertex.setIsVisted(true);
int counter = 0;
firstVertex.setPathlength(counter);
queue.add(firstVertex);
System.out.println("最短路径为:");
String path;
while (!queue.isEmpty()) {
Vertex v = queue.poll();
// 得到队顶的path和Counter,用于下面for循环的赋值
path = v.getFrom();
counter = v.getPathlength();
System.out.println(v.getFrom() + "的路径长度为 " + v.getPathlength());
ArrayList al = g.getAdjacentVertex(v);
for (Vertex vertex : al) {
if (!(vertex.getIsVisted()))// 没被访问过
{
vertex.setIsVisted(true);
vertex.setPathlength(counter + 1);
queue.add(vertex);
}
}
}
}
}
测试数据为下图: