本文主要介绍有权图中的最短路径问题,由于Dijkstra算法是广度优先搜索的改进算法,所以本文先介绍一下普通的bfs算法。
一.BFS算法
说到BFS算法,其实普通的BFS算法有些类似于二叉树中的层次遍历。因此可以用队列实现,具体步骤如下:
1.访问初始结点v,并标记结点v为已访问;
2.结点v入队列
3.当队列为空时,继续执行,否则算法结束(用while循环判别)
4.出队列时,取得队头结点u
5.查找结点u的第一个邻接结点w,若w不存在则转3,否则执行如下操作:
a.若结点w尚未被访问,则访问结点w,并标记为已访问,结点w入队列
b.查找结点u的其它邻接结点,转到5进行判断。
如下图所示为一无向图,以bfs该无向图为例进行分析,首先为便于遍历,则将图上的A-H分别以0-7来代替。首先根据图的连通情况,用邻接矩阵来记录整个图,假设邻接矩阵为matrix,用一个数组str存储元素,与相应下标一一对应。用一个队列q来按照访问顺序进行存储元素,用另一个队列visited来存储元素,用来判断当前元素是否访问过。
初始化将源点s存入visited和q中。当访问第一个元素s时,依次遍历所有元素,看是否连通,如果连通,则判断是否在visited中,如果没有,则在visited和p中加入未访问过的连通元素。遍历结束然后删除队头元素。依次循环直到队列q为空。
代码实现:
import java.util.*;
public class BFSfisrt {
private Queue q;
private Queue<Integer> visited;
public BFSfisrt() {
// TODO Auto-generated constructor stub
q=new LinkedList();
visited=new LinkedList<>();
}
private int getIndex(char v,char[] str)
{
for(int i=0;i<str.length;i++)
{
if(v==str[i])
return i;
}
return -1;
}
public void bfs(int[][] matrix,char v,char[] str)
{
int i=getIndex(v,str);
if(i==-1) return;
q.add(i);
visited.add(i);
System.out.print(str[i]+" ");
while(!q.isEmpty())
{
int u=(int)q.remove();
for(int j=0;j<str.length;j++)
{
if(matrix[u][j]==1 && (!visited.contains(j)))
{
q.add(j);
visited.add(j);
System.out.print(str[j]+" ");
}
}
}
}
}
测试代码:
public class BFSfirstTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[][] matrix= {{0,1,1,0,0,0,0,0},{1,0,0,1,1,0,0,0},
{1,0,0,0,0,1,1,0},{0,1,0,0,0,0,0,1},{0,1,0,0,0,0,0,1},
{0,0,1,0,0,0,1,0},{0,0,1,0,0,1,0,0},{0,0,1,1,0,0,0,0}};
char[] str= {'A','B','C','D','E','F','G','H'};
BFSfisrt bfs=new BFSfisrt();
bfs.bfs(matrix, 'A', str);
}
}
测试结果:
二.Dijkstra算法
对于普通的BFS算法,无法解决有权图中的最短路径问题,因为它不能保证处于队列前面的顶点是最接近源s的顶点,所以需要对BFS加以改进,保证每次访问的节点到源点的长度是最短的。该算法具体步骤如下:
1.声明一个数组distance来保存源点到各个顶点的最短距离和一个已经找到最短路径的顶点的队列visited,以及一个用于存储具体路线的HashMap集合path;
2.初始时,源点s的路径权重设置为0(distance[0]),依次遍历每个点m,找到s能直接到达的边s-m所对应的顶点m,将distance[m]设置为weight[s][m](weight是图的邻接矩阵),其余顶点设为Integer.MAX_VALUE;初始化队列visited,初始时只有一个s;path初始时,以每个点为key值,所对应的value都加入点s
3.然后从distance数组中选择visited点以外点的最小值,则该值就是s到该值对应的顶点的最短路径,并且把该值加入visited中,此时完成一个顶点。
4.然后,需要看新加入的顶点是否可以到达其他顶点,并且看看通过该顶点到达其他点的路径长度是否比源点直接到达更短,如果更短则更新distance中对应点的值,以及更新path中对应key值得value。
5.重复3、4步骤,直到visited中包含了图中的所有顶点
思路:Dijkstra算法采用了贪婪法--总是选取最接近源点的顶点。同时使用优先队列并按照到s的距离来存储未被访问过的顶点--为了求未被访问的最接近源点的点可以通过维护最小堆的方式,每次取出堆顶元素之后,然后进行更新堆,但是本文的例子是通过visited队列来记录是否访问过,以及在distance数组中对未访问过的顶点求最小值。
本文以下图所示介绍Dijkstra算法,并且通过一个遍历,使如下各点分别都做一次源点s,分别求源点s到各点的最短路径以及对应的线路。
代码实现:
import java.util.*;
public class Dijkstra {
private Queue visited;
int[] distance;
public Dijkstra(int len) {
// TODO Auto-generated constructor stub
visited=new LinkedList();
distance=new int[len];
}
private int getIndex(Queue q,int[] dis)
{
int k=-1;
int min_num=Integer.MAX_VALUE;
for(int i=0;i<dis.length;i++)
{
if(!q.contains(i))
{
if(dis[i]<min_num)
{
min_num=dis[i];
k=i;
}
}
}
return k;
}
public void dijkstra(int[][] weight,Object[] str,int v)
{
HashMap path;
path=new HashMap();
for(int i=0;i<str.length;i++)
path.put(i, "");
//初始化路径长度数组distance
for(int i=0;i<str.length;i++)
{
path.put(i, path.get(i)+""+str[v]);
if(i==v)
distance[i]=0;
else if(weight[v][i]!=-1)
{
distance[i]=weight[v][i];
path.put(i, path.get(i)+"-->"+str[i]);
}
else
distance[i]=Integer.MAX_VALUE;
}
visited.add(v);
while(visited.size()<str.length)
{
int k=getIndex(visited,distance);//获取未访问点中距离源点最近的点
visited.add(k);
if(k!=-1)
{
for(int j=0;j<str.length;j++)
{
if(weight[k][j]!=-1)//判断k点能够直接到达的点
{
//通过遍历各点,比较是否有比当前更短的路径,有的话,则更新distance,并更新path。
if(distance[j]>distance[k]+weight[k][j])
{
distance[j]=distance[k]+weight[k][j];
path.put(j, path.get(k)+"-->"+str[j]);
}
}
}
}
}
for(int h=0;h<str.length;h++)
{
System.out.printf(str[v]+"-->"+str[h]+":"+distance[h]+" ");
if(distance[h]==Integer.MAX_VALUE)
System.out.print(str[v]+"-->"+str[h]+"之间没有可通行路径");
else
System.out.print(str[v]+"-"+str[h]+"之间有最短路径,具体路径为:"+path.get(h).toString());
System.out.println();
}
visited.clear();
}
}
测试代码:
public class DijkstraTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[][] weight= {{0,-1,10,-1,30,100},{-1,0,5,-1,-1,-1},{-1,-1,0,50,-1,-1},{-1,-1,-1,0,-1,10},
{-1,-1,-1,20,0,60},{-1,-1,-1,-1,-1,0}};
String[] str= {"V1","V2","V3","V4","V5","V6"};
int len=str.length;
Dijkstra dijkstra=new Dijkstra(len);
//依次让各点当源点,并调用dijkstra函数
for(int i=0;i<str.length;i++)
{
dijkstra.dijkstra(weight, str, i);
}
}
}
测试结果:
以上是对广度优先搜索以及以广度优先搜索为基础的dijkstra算法的讲解,dijkstra算法的缺点如下:
a.盲目搜索导致的时间浪费和资源浪费
b.不能处理边权值为负的情况,因为导致上述算法中求得的最短路径未必是最短路径。
转载请注明:转自http://blog.csdn.net/carson0408/article/details/78634650