Dijkstra算法——《算法導論》學習心得(十三)

個人blog遷移到 www.forwell.me  

        這兩天在做一個項目,關於北京市出租車的,然後用到了Dijkstra算法,所以這篇文章就先寫Dijkstra算法了。在大二下的時候學了數據結構,書裏面也講了Dijkstra算法,但是當時怎麼也沒理解,結果考試的時候就考了,哎蛋疼!現在用到了,又得硬着頭皮去學,結果很快弄明白了,只是在寫代碼時出了一些很低級的錯誤,調Bug用了不少時間。最後總結只能說:不是你不會,而是沒到你非會不可的地步!在這篇文章裏我就用實際的項目給大家講Dijkstra算法。

背景:

        迪傑斯特拉算法是由荷蘭計算機科學家狄克斯特拉於1959 年提出的,因此又叫狄克斯特拉算法。是從一個頂點到其餘各頂點的最短路徑算法,解決的是有向圖中最短路徑問題。迪傑斯特拉算法主要特點是以起始點爲中心向外層層擴展,直到擴展到終點爲止。(我覺得這個算法實質就是每一次你就在所有的路里面找一條最短的路走,這條路走完就不要再走了,在找一條,最後走到你要到的終點就行了,這個過程中沒有任何的啓發性,所以算法計算量就會很大,對於大型地圖是不實際的,都得需要優化。)

算法描述

        算法思想:設G=(V,E)是一個帶權有向圖,把圖中頂點集合V分成兩組,第一組爲已求出最短路徑的頂點集合(用S表示,初始時S中只有一個源點,以後每求得一條最短路徑 就將加入到集合S中,直到全部頂點都加入到S中,算法就結束了),第二組爲其餘未確定最短路徑的頂點集合(用U表示),按最短路徑長度的遞增次序依次把第二組的頂點加入S中。在加入的過程中,總保持從源點vS中各頂點的最短路徑長度不大於從源點vU中任何頂點的最短路徑長度。此外,每個頂點對應一個距離,S中的頂點的距離就是從v到此頂點的最短路徑長度,U中的頂點的距離,是從v到此頂點只包括S中的頂點爲中間頂點的當前最短路徑長度。

算法步驟:

        a.初始時,S只包含源點,即S{v}v的距離爲0U包含除v外的其他頂點,即:U={其餘頂點},若vU中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值爲

        b.U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是vk的最短路徑長度)。

        c.k爲新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(經過頂點k)比原來距離(不經過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。

        d.重複步驟bc直到所有頂點都包含在S中。

執行動畫過程如下圖:


我的運用:

在這個項目中我就是需要找到兩個點之間的最短路徑,然後在圖我是用一個ArrayList來存的,沒有用連接表或者是矩陣,因爲我覺得圖就是邊的集合,然後再用一個ArrayList來存圖的點集,畢竟在算法中我們需要用點,我不能去圖裏面遍歷所有的邊,然後在通過邊來拿點,應爲這樣太慢了,整個北京市有218965條路,每一條路還有幾百上千的點組成,這個數據就有點大了,尤其對於Dijkstra算法,頂點多了,算法絕對慢。未來解決這些問題,我還建立了兩個索引,用map來做的,一個是用來判斷一個新的點是否已經加入到頂點集,另一個使用來判斷兩個點是否直接連接!

先看一些實體類吧:

Point類:

<span style="font-size:14px;"><span style="font-size:14px;">package com.tangbo;

public class Point {
	private String pointId;//longitude+latitude構建出來的
	private String date;//這個點創造的實際日期,項目需要的屬性,大家可以不用管
	private double longitude;//經度
	private double latitude;//緯度
	private String isHavePeople;//項目需要的屬性,大家可以不用管
	private String roadNum;//項目需要的屬性,大家可以不用管
	private double ObservProbability;//項目需要的屬性,大家可以不用管
	private int position;//記錄這個點在該路里面是第幾個點,因爲一條路是由點集構成的,我需要知道這個點在這條路的那個位置
	public Point() {
		super();
	}
	public Point(String pointId)
	{
		this.pointId = pointId;
	}
	public double getLongitude() {
		return longitude;
	}
	public void setLongitude(double longitude) {
		this.longitude = longitude;
	}
	public double getLatitude() {
		return latitude;
	}
	public void setLatitude(double latitude) {
		this.latitude = latitude;
	}
	public String getDate() {
		return date;
	}
	public void setDate(String date) {
		this.date = date;
	}
	public String getIsHavePeople() {
		return isHavePeople;
	}
	public void setIsHavePeople(String isHavePeople) {
		this.isHavePeople = isHavePeople;
	}
	public String getRoadNum() {
		return roadNum;
	}
	public void setRoadNum(String roadNum) {
		this.roadNum = roadNum;
	}
	
	public double getObservProbability() {
		return ObservProbability;
	}
	public void setObservProbability(double observProbability) {
		ObservProbability = observProbability;
	}
	public int getPosition() {
		return position;
	}
	public void setPosition(int position) {
		this.position = position;
	}
	
	public String getPointId() {
		return pointId;
	}
	public void setPointId(String pointId) {
		this.pointId = pointId;
	}
	@Override
	public String toString() {
		return longitude + "," + latitude;
	}
}
</span></span>

LineSegment(路段)類:

<span style="font-size:14px;"><span style="font-size:14px;">package com.tangbo;

public class LineSegment {
	private String roadNum;//路段的編號
	private Point starPoint;//起點
	private Point endPoint;//終點
	private double disc;//這段路有多長
	public LineSegment() {
		super();
	}
	
	public LineSegment(Point starPoint, Point endPoint,double disc) {
		super();
		this.starPoint = starPoint;
		this.endPoint = endPoint;
		this.disc = disc;
	}

	public LineSegment(Point starPoint, Point endPoint) {
		super();
		this.starPoint = starPoint;
		this.endPoint = endPoint;
		this.disc = Util.calcuDistByCoordinate(starPoint, endPoint);
	}
	public Point getStarPoint() {
		return starPoint;
	}
	public void setStarPoint(Point starPoint) {
		this.starPoint = starPoint;
	}
	public Point getEndPoint() {
		return endPoint;
	}
	public void setEndPoint(Point endPoint) {
		this.endPoint = endPoint;
	}
	public String getRoadNum() {
		return roadNum;
	}
	public void setRoadNum(String roadNum) {
		this.roadNum = roadNum;
	}
	public double getDisc() {
		return disc;
	}
	public void setDisc(double disc) {
		this.disc = disc;
	}
	@Override
	public String toString() {
		return "LineSegment [roadNum=" + roadNum + ", starPoint=" + starPoint
				+ ", endPoint=" + endPoint + ", disc=" + disc + "]";
	}
}
</span></span>

ShortPath類:

<span style="font-size:14px;"><span style="font-size:14px;">package com.tangbo;

import java.util.ArrayList;

public class ShortPath {
	private ArrayList<Point> points;//路徑是由點構成的
	private double disc;//距離
	public ShortPath() {
		super();
		points = new ArrayList<Point>();
	}
	public ShortPath(Point point) {
		points = new ArrayList<Point>();
		points.add(point);
		this.disc=ConfigurationFiles.FLAG;
	}

	public ArrayList<Point> getPoints() {
		return points;
	}

	public void setPoints(ArrayList<Point> points) {
		this.points = points;
	}

	public double getDisc() {
		return disc;
	}

	public void setDisc(double disc) {
		this.disc = disc;
	}

	public void addNode(Point parent) {
		if(points==null)
			points = new ArrayList<Point>();
		points.add(0, parent);
	}

	public void addWeight(double desc) {
		if(desc==ConfigurationFiles.FLAG)
		{
			disc = ConfigurationFiles.FLAG;
		}else
		{
			disc= disc+desc;
		}
		
	}

	public void print() {
		System.out.println("開始點");
		for(int i=0;i<points.size();i++)
		{
			System.out.println(points.get(i));
		}
		System.out.println("結束!");
	}
	
	@Override
	public String toString() {
		
		String temp="";
		for(int i=0;i<points.size();i++)
		{
			temp=temp+points+"#";
		}
		
		return temp;
	}	
}
</span></span>
Dijkstra具體算法:

<span style="font-size:14px;"><span style="font-size:14px;">package com.tangbo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.util.JSON;

public class Dijkstra{
	public  ArrayList<LineSegment> map=null;//圖
	public  ArrayList<Point> pointCollection = new ArrayList<Point>();;//頂點集
	
	public  ArrayList<Point> bluePoints = null;//還未加入的點
	public  ArrayList<ShortPath> currentShortPaths = null;//存放目前的所有的最短路徑集合
	public  int count=0;
	public  Map<String, Integer> pointIndex = new HashMap<String, Integer>();//建立點的索引,用來判斷一個點是否已經加入頂點集
	public  Map<String, Double> roadIndex = new HashMap<String, Double>();//建立路段索引,用來判斷兩條路是否連接
	public  void iniMap() {
		//我的所有路段是存在MongoDB裏面,我需要把數據讀出來,然後把地圖初始化
		map = new ArrayList<LineSegment>();
		pointIndex = new HashMap<String, Integer>();
		String json = "{MapID:\"605751\"}";
		DBCursor dbCursor = ConfigurationFiles.mongoDB4CRUDRoadInformation.getCollections().find((DBObject)JSON.parse(json));
		//DBCursor dbCursor = ConfigurationFiles.mongoDB4CRUDRoadInformation.getCollections().find();
		System.out.println("dbCursor:"+dbCursor.count());
		while(dbCursor.hasNext())
		{
			DBObject dbObject = dbCursor.next();
			ArrayList<Point> pointsTemp= Util.getTrajectoryByDBObject(dbObject);
			LineSegment lineSegment = new LineSegment();
			Point starPoint = pointsTemp.get(0);
			Point endPoint = pointsTemp.get(pointsTemp.size()-1);
			lineSegment.setStarPoint(starPoint);//加入起點
			lineSegment.setEndPoint(endPoint);//加入終點
			double disc = Util.calcuDistByCoordinate(starPoint, endPoint);
			lineSegment.setDisc(disc);//計算距離
			lineSegment.setRoadNum(pointsTemp.get(0).getRoadNum());//設置路段標示
			map.add(lineSegment);
			addPoint(starPoint, endPoint,disc);
		}
	}
	//判斷兩個點是否已經加入到頂點集中
	public  void addPoint(Point starPoint,Point endPoint,double disc)
	{
		String starPointId = starPoint.getLongitude()+""+starPoint.getLatitude();
		String endPointId = endPoint.getLongitude()+""+endPoint.getLatitude();
		roadIndex.put(starPointId+"#"+endPointId, disc);
		if(pointIndex.get(starPointId)==null)//利用pointIndex判斷這個點是否已經加入到頂點集中
		{
			pointCollection.add(starPoint);//將起點加入到
			starPoint.setPointId(starPointId);
			pointIndex.put(starPointId, pointCollection.size()-1);//然後在pointMap裏面進行備註,用於快速查找
			
		}
		if(pointIndex.get(endPointId)==null)
		{
			pointCollection.add(endPoint);//將終點加入到
			endPoint.setPointId(endPointId);
			pointIndex.put(endPointId, pointCollection.size()-1);//然後在pointMap裏面進行備註,用於快速查找
		}
	}
	//Dijkstra算法開始
	public ShortPath dijkstra(String startPointID,String endPointID) 
	{
		ShortPath shortPath = null;
		bluePoints = new ArrayList<Point>();//還未加入的點
		currentShortPaths = new ArrayList<ShortPath>();//存放目前已經找到的所有最短路徑
		//開始初始化紅點集和藍點集
		System.out.println("pointCollection.size():"+pointCollection.size());
		if(pointIndex.get(startPointID)!=null)
		{
			System.out.println("開始節點沒有找到!");
			System.out.println(pointCollection.get(pointIndex.get(startPointID)));
		}else
		{
			System.out.println("沒有找到!");
			System.exit(0);
		}
		for(int i=0;i<pointCollection.size();i++)//初始化藍點集
		{
			Point point  = pointCollection.get(i);
			bluePoints.add(point);
		}
		bluePoints.remove(pointCollection.get(pointIndex.get(startPointID)));//講開始節點移出藍點集
		ShortPath tempShortPath = new ShortPath(pointCollection.get(pointIndex.get(startPointID)));//開始節點本身是一條最短路徑,加入到currentShortPaths
		currentShortPaths.add(tempShortPath);
		
		while(bluePoints.size()>0)
		{
			System.out.println("bluePoints.size():"+bluePoints.size());
			double minDis[][]=new double[currentShortPaths.size()][bluePoints.size()];//保存每個parent的shortpath的最後一個point與bluepoints之間的最小距離。
			for(int i=0;i<minDis.length;i++)//初始化每一個藍點集的權重
			{
				for(int j=0;j<minDis[0].length;j++)
				{
					minDis[i][j]=Integer.MAX_VALUE;
				}
			}
			for(int i=0;i<currentShortPaths.size();i++)//給每一個藍點集的節點計算到所有最短路徑的距離
			{
				ShortPath sortPH=currentShortPaths.get(i);
				ArrayList<Point> arrayPoints=sortPH.getPoints();
				Point lastPoint=arrayPoints.get(arrayPoints.size()-1);
				double dis=ConfigurationFiles.FLAG;//ConfigurationFiles.FLAG=-1,代表這個條路不同
				int y=0;//用來保存bluepoint的位置
				for(int j=0;j<bluePoints.size();j++)
				{
					Point bluePoint=bluePoints.get(j);
					
					if(dis==ConfigurationFiles.FLAG)
					{
						dis=getWeight(lastPoint, bluePoint);
						if(dis!=ConfigurationFiles.FLAG)
						{
							y=j;
						}
						continue;
					}
					if(dis!=ConfigurationFiles.FLAG)
					{
						double tempDis=getWeight(lastPoint, bluePoint);
						if(tempDis!=ConfigurationFiles.FLAG&&tempDis<dis)
						{
							dis=tempDis;
							y=j;
						}
					}
				}
				if(dis!=ConfigurationFiles.FLAG)
				{
					minDis[i][y]=dis+sortPH.getDisc();
				}
			}
			double mindis=minDis[0][0];
			int qhlRedPoint=0;
			int qhlBluePoint=0;
			for(int i=0;i<minDis.length;i++)
			{
				for(int j=0;j<minDis[i].length;j++)
				{
					if(minDis[i][j]<mindis)
					{
						mindis=minDis[i][j];
						qhlRedPoint=i;
						qhlBluePoint=j;
					}
				}
			}
			ShortPath newShortPath=copyShortPath(currentShortPaths.get(qhlRedPoint));
			
			Point newBluePoint=bluePoints.get(qhlBluePoint);
			newShortPath.getPoints().add(newBluePoint); //把新的點加入到這個新的最短路徑中
			newShortPath.setDisc(mindis);  //設置距離
			currentShortPaths.add(newShortPath);//添加最短路徑
			bluePoints.remove(qhlBluePoint);//移出這個藍點
			if(newBluePoint.getPointId().equals(endPointID))
			{
				shortPath =  newShortPath;
				break;
			}	
		}
		return shortPath;
	}
	//複製一條最短路徑
	private ShortPath copyShortPath(ShortPath shortPath) {
		ShortPath tempShortPath = new ShortPath();
		tempShortPath.setDisc(shortPath.getDisc());
		Point tempPoint;
		for(int i=0;i<shortPath.getPoints().size();i++)
		{
			tempPoint = new Point();
			tempPoint  = shortPath.getPoints().get(i);
			tempShortPath.getPoints().add(tempPoint);
		}
		
		return tempShortPath;
	}
	//獲得兩個點之間的距離,如果這兩個點不直接相連就是他們不是在一個LineSegment上,那麼他的值爲ConfigurationFiles.FLAG,代表不通
	public  double getWeight(Point star,Point end)
	{
		double result = ConfigurationFiles.FLAG;
		String starPointId = star.getLongitude()+""+star.getLatitude();
		String endPointId = end.getLongitude()+""+end.getLatitude();
		String situation1 = starPointId+"#"+endPointId;
		String situation2 = endPointId+"#"+starPointId;
		if(roadIndex.get(situation1)!=null)//通過索引查看這兩個點是否直接相連
		{
			result = roadIndex.get((starPointId+"#"+endPointId));
		}
		if(roadIndex.get(situation2)!=null)
		{
			result=roadIndex.get((endPointId+"#"+starPointId));
		}
		if(result!=ConfigurationFiles.FLAG)
		{
			count++;
		}
		return result;
	}
}</span></span>
        源碼下載:http://pan.baidu.com/s/1pJ2sBd5。基本上這個算法就是這樣,當然還得加一下局部優化算法,這個算法才能真正用於實際的工程,否則算法運行時間太長了!如果大家還有什麼想法,歡迎來一起交流:[email protected]





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章