单元Dijkstra算法求最短路径的的Java实现

上一篇写了无权值的最短路径的求法,是利用广度优先搜索的方法使用队列实现的,当有权值时,我们通常使用Dijkstra算法来求解最短路径的问题,这里我们假设所有的权值都是正值。
首先,在节点类改变了权值的成员变量pathLength起始为INFINITY(INFINITY设为10000),这样有助于寻找未访问节点中权值最小的点。边节点要增加一个表示权值的成员变量weight。
其次,在图类Graph中增添了两个方法,第一个是返回两节点之间的边getEdge(String start,String end),第二个是返回未访问节点中的权值最小的节点。并且修改Graph的建图方法,每条边加入权值的输入。

最后是主算法dijkstra();,并增添了一个输出方法以供输出完整的具体路径和路径长度。

Graph中增添的两个方法

//返回两个节点之间的边
	public Edge getEdge(Vertex start, Vertex end)
	{
		Edge edge = start.first;
		if(edge.nextEdge == null)
			return edge;
		else//说明不是第一条,需要通过链表找出另一条边
		{
			for(Edge e = edge; e != null ; e = e.nextEdge)
			{
				if(e.getTo().equals(end.getFrom()))
				{
					//找到符合条件的Edge赋给edge并退出循环
					edge = e;	
					break;
				}
			}				
		}
		return edge;
	}
	
	//返回未访问的节点中权值最小的节点
	public Vertex getSmallestUnvisitedVertex()
	{

		Vertex smallestVertex = null;
		//先找到第一个未被访问过的节点
		
		for(int i = 0 ; i < v.length ; i ++)
		{		
			if(!v[i].getIsVisted())
			{
				smallestVertex = v[i];
				break;
			}
		}
		
		for(int i= 0 ;i<v.length;i++)
		{
			if(!v[i].getIsVisted())
			{
				if(v[i].getPathlength() < smallestVertex.getPathlength())
					smallestVertex = v[i];
			}
		}
		return smallestVertex;
		
	}

主算法以及测试:

package com.Algorithm.ShortestPath;


/*
 *单元 dijkstra算法求最短路径
 */

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;

public class ValuedShortestPath {

	Graph g;
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ValuedShortestPath vsp = new ValuedShortestPath();
		System.out.println("输入路径的起点和终点:");
		Scanner input = new Scanner(System.in);
		String first = input.next();
		String end = input.next();
		vsp.dijkstra(first);
		vsp.printPath(first, end);
		input.close();
	}

	//构造函数
	public ValuedShortestPath()
	{
		g = new Graph();
		g.buildGraph();
	}
	
	public void dijkstra(String first)
	{
		//通过初始节点名称找到初始节点并将其路径长度设为初始长度0
		Vertex ver = g.v[g.findvIndex(first)];
		ver.setPathlength(0);
		
		System.out.println("从"+first+"出发的到各个节点的最短路径为:");
		for(;;)
		{
			//对于所有的未被访问过的节点进行操作
			//首先找到权值最小的节点,第一次就是第一个节点ver,以后每次都会更新			
			ver = g.getSmallestUnvisitedVertex();
			if(ver == null) //如果找不到,说明所有节点都被访问过,结束循环
				break;
			ver.setIsVisted(true);			
			//输出到控制台
			System.out.println( ver.getFrom()+"的路径长度为 "+ver.getPathlength()+",上一个节点是"+ver.getPath()+" ");			
			ArrayList<Vertex> al = g.getDirectedAdjacentVertex(ver);
			for(Vertex vertex : al)
			{
				if(!vertex.getIsVisted())//对于没被访问过的节点
				{
					//找到相邻节点对应的边上的权值 
					Edge edge = g.getEdge(ver, vertex);
					int weight = edge.getWeight();
					//如果原节点+边权值仍小于这个相邻节点的值,则更新节点的值
					if(vertex.getPathlength() > weight + ver.getPathlength() )
						{
							vertex.setPathlength(weight+ver.getPathlength());
							//将这个相邻节点的path设为ver
							vertex.setPath(ver.getFrom());
						}															
				}
			}											
			
		}
		
		
	}
	
	//通过输入开始节点和结束节点,输出最短路径的具体路径和路径长度
	public void printPath(String start,String end)
	{
		System.out.println("从"+start+"到"+end+"的最短路径:");
		//首先找到尾节点并得到最后一个path路径
		Vertex tmpVertex = g.v[g.findvIndex(end)];		
		String tmpPath = tmpVertex.getFrom();
		System.out.println("路径长度为" + tmpVertex.getPathlength() );
		System.out.println("具体路径为:");
		while( true )
		{
			//每次输出路径并更新tmpVertex和tmpPath
			System.out.print(tmpPath + " ");
			if(tmpVertex.getPath() != null)
				tmpVertex = g.v[g.findvIndex(tmpVertex.getPath())];
			else 
				break;
			tmpPath = tmpVertex.getFrom();
		}
		
	}
	
}
测试结果:

原图为:

输入顶点数和边数:7 12
输入顶点名称:
v1 v2 v3 v4 v5 v6 v7
输入起点、终点和边的权值:v1 v2 2
输入起点、终点和边的权值:v1 v4 1
输入起点、终点和边的权值:v2 v4 3
输入起点、终点和边的权值:v2 v5 10
输入起点、终点和边的权值:v3 v1 4
输入起点、终点和边的权值:v3 v6 5
输入起点、终点和边的权值:v4 v3 2
输入起点、终点和边的权值:v4 v5 2
输入起点、终点和边的权值:v4 v7 4
输入起点、终点和边的权值:v4 v6 8
输入起点、终点和边的权值:v5 v7 6
输入起点、终点和边的权值:v7 v6 1
输入路径的起点和终点:
v1 v6
从v1出发的到各个节点的最短路径为:
v1的路径长度为 0,上一个节点是null 
v4的路径长度为 1,上一个节点是v1 
v2的路径长度为 2,上一个节点是v1 
v3的路径长度为 3,上一个节点是v4 
v5的路径长度为 3,上一个节点是v4 
v7的路径长度为 5,上一个节点是v4 
v6的路径长度为 6,上一个节点是v7 
从v1到v6的最短路径:
路径长度为6
具体路径为:
v6 v7 v4 v1 

这里要说明的一点是,最后输出的路径是逆序的,也就是从v1到v6的最短路径应该为v1 v4 v7 v6,这是算法所致,这里不再修改。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值