Dijkstra算法

由于学校的机器学习课程马上要讲搜索,涉及到A*算法,索性自己先找来资料读。


A*算法是启发式算法的一种,启发式算法是在当前节点搜索下一个节点的时候,利用一个启发式函数,选择cost最少的点,作为下一个去到的地方。(这里是说有很多个点可以选择作为下一跳,但我们想找到一个best point)

A*算法可以在平面上,找到一条经过多个节点的,成本最少的路径。


f(n) = g(n) + h(n)

f(n)是每个candidate的estimate value,consisted by two part.

g(n):从start到当前点的代价(例如距离当前点的距离)

h(n): 当前点到目标点的估值 


A*算法在每次选择下一跳点是从所有已经探知,但是未搜索过的点中,选取f值最小的点。用一个优先队列存储节点。每次从优先队列中pop出队首元素,计算它的candidate子节点的g,h,f值,直到队列为空。


步骤:

initialized:将起点s放到open表里,close表为空。

1. 从open表里取出一个节点,这个节点要满足f值最小,将其放进close表。

2. 把这个节点的后续节点,把不在close表里的节点,全都放进open表里,同时计算他们的估值f,并把open表按照f升序排列,最小的放前面。

3. 重复1 2 。直到open表为空。


例子 //图片是引用rickone


初始化,将起点v0放进open表中,close为空。

1. 从open表中选出一个节点,这里只有v0,所以f_v0最小,将其放进close表中。

2. v0的后续节点有(v5,100), (v4,30),(v2,10)放进open表中,并且按照升序排序(v2,10),(v4,30),(v5,100)。

(重复1,2)

1.从open选出f值最小的v2,放进close表,于是close表变为(v0,0),(v2,10)

2. v2的后续节点有v3,从v2到v3的成本为50,加上v1到v2的10,所以v3的f值为10+50=60.将(v3,60)放进open表并排序,得到新的open表为(v4,30),(v3,60),(v5,100)

(重复1,2)

1.从open选出f值最小的v4放进close表,close表变成(v0,0),(v2,10),(v4,30)

2. v4的后续节点有v3和v5.从v4去到v5的成本是60,加上从v0到v4的成本是30,所以v5的f值等于60+30=90。同样,v3的f值为30+20=50.将(v5,90)和(v3,50)放入open表,得到新的open表为(v3,50),(v3,60),(v5,90),(v5,100)。

(重复1,2

1.从open选出f值最小的v3放进close表,close表变成(v0,0),(v2,10),(v4,30),(v3,50)。

2. v3的后续节点有v5,从v3到v5的成本为10,加上v3自己的f值是50,所以v5的f值变为50+10=60,放入open表并排序得到新open表是(v5,60),(v5,90),(v5,100)。(这里的(v3,60)因为v3被放进了close表里了,所以从open表中删除了

(重复1,2)

1.从open选出f值最小的(v5,60)放入close表,close表变成了(v0,0),(v2,10),(v4,30),(v3,50),(v5,60)。

2.v5没有后续节点,因此没有节点可以放入open表中,于是open表为空,算法结束。


首先是构建图数据结构:

这个是Graph类,main函数在这里面。

public class Graph {
	int numVertex = 0;//节点总数
	boolean isDirect = true;//是无向图
	static Vertex[] vertexList;//节点表
	//default constructive function
	public Graph(){
		vertexList = new Vertex[20];
	}
	//how many nodes
	public Graph(int n){
		vertexList = new Vertex[n];
	}
	//if it is a direct graph
	public Graph(int n, boolean isDirect){
		vertexList = new Vertex[n];
		this.isDirect = isDirect;
	}
	public Vertex[] getVertexList(){
		return vertexList;
	}
	
	//添加节点
	public void addVertex(Vertex vertex){
		vertex.setIndex(numVertex);//设置节点序号
		vertexList[numVertex] = vertex;
		numVertex++;
	}
	//添加边
	public void addEdge(int start, int end, int weight){
		vertexList[start].addAdj(vertexList[end], weight);
		//如果是无向图,还要双向添加边
		if(isDirect == false) vertexList[end].addAdj(vertexList[start], weight);
	}

	public int getVertsCount(){
		return vertexList.length;
	}
	public void displayGraph(){
		for(int i = 0; i<vertexList.length; i++){
			printVertex(vertexList[i]);
		}
	}
	public void printVertex(Vertex vertex){
		ArrayList<Edge> next = vertex.getAdj();
		if(next == null){System.out.println(vertex.toString()+"无邻接点");}
		else{
			System.out.println(vertex.toString() + "有邻接点:");
			for(int i = 0; i<next.size();i++){
				Edge adjEdge = next.get(i);
				System.out.println("\t顶点" + adjEdge.end_label+"," + "权重:" + adjEdge.weight);
			}
			System.out.println();
		}
	}
	
	static class Vertex{
		public char label;
		public boolean isVisted;
		public int index;
		private ArrayList<Edge> next = null;
		//A*中的g,h,f值
		int g_score;
		int h_score;
		int f_score;
		int d;//dijkstra中的d值
		//构造函数
		public Vertex(char label){
			this.label = label;
			isVisted = false;
		}
		public void addAdj(Vertex end, int weight){//end是终点
			Edge edge = new Edge();//实例化和自己与邻接顶点之间的边
			if(next == null){
				next = new ArrayList<Edge>();
			}
			edge.setEdge(end, weight);
			next.add(edge);	
		}
		public ArrayList<Edge> getAdj(){
			return next;
		}
		public void setIndex(int index){
			this.index = index;
		}
		public String toString(){
			return "顶点" + label + ",下标:" + index + ":";
		}	
	} 
	
	static class Edge{
		int end;//终点
		char end_label;
		int weight;
		public int getEnd(){
			return end;
		}
		public void setEdge(Vertex ver, int weight){
			this.end = ver.index;
			this.end_label = ver.label;
			this.weight = weight;	
		} 
	}

	public static void main(String[] args){
		int c = 'A' - 1;
		Vertex vertex;
		Graph myGraph = new Graph(6);//10个节点的无向图
		for(int i = 0; i<6; i++){
			c++;
			vertex = new Vertex((char)c);
			myGraph.addVertex(vertex);
		}
		myGraph.addEdge(0, 5, 100);
		myGraph.addEdge(0, 4, 30);
		myGraph.addEdge(0, 2, 10);
		myGraph.addEdge(1, 2, 5);
		myGraph.addEdge(2, 3, 50);
		myGraph.addEdge(3, 5, 10);
		myGraph.addEdge(4, 3, 20);
		
		//myGraph.displayGraph();
		Vertex[] s = Dijkstra.dijkstra(myGraph,0);
		for(int i = 0; i<s.length;i++){
			System.out.println("label:"+s[i].label+",d:"+s[i].d);
		}
	}

}

A*还没写好,先放Dijkstra排序

这是一种求解Single-source shortest path problem的算法。

Dijkstra类如下

public class Dijkstra {
	
	public static Vertex[] dijkstra(Graph graph, int src){
		int count = 0;
		Vertex[] s = new Vertex[graph.getVertsCount()];
		Vertex[] V = Graph.vertexList;
		for(int v = 0; v<V.length; v++){
			V[v].d = Integer.MAX_VALUE;
		}
		V[src].d = 0;//source的距离设置为0
		makeMinHeap(V,V.length);//建堆
		while(V.length>0){
			Vertex minHeapNode = Dijkstra.extractMin(V);
			s[count] = minHeapNode;
			count++;
			V = Arrays.copyOfRange(V,0,V.length-1);
			if(V.length == 0) break;
			//最后一个节点的d值在移除上一个节点的时候已经改变了,
			//而轮到移除自己的时候,没有机会改变d值了
			//所以判断如果V为空,就直接break
			minHeapDown(V,0,V.length);
			ArrayList<Edge> next = minHeapNode.getAdj();
			if(next != null){
				for(int i = 0; i< next.size(); i++){
					int v = next.get(i).end;//i号后续点的index
					int weight = next.get(i).weight;
					if(isInMinHeap(V,v) && minHeapNode.d!=Integer.MAX_VALUE){
						adjustD(V,minHeapNode,v,weight);//调整d
					}
				}
			}
			makeMinHeap(V,V.length);
		}
		return s;
	}
	
	//修改d值
	public static void adjustD(Vertex[] minHeap, Vertex minHeapNode, int v, int weight){
		int i;
		for(i = 0; i<minHeap.length; i++){
			if(minHeap[i].index == v){
				break;
			}
		}
		if(minHeap[i].d > minHeapNode.d + weight){
			minHeap[i].d = minHeapNode.d + weight;
		}
	}
	//判断v是否在最小堆中
	public static boolean isInMinHeap(Vertex[] minHeap, int v){
		if(minHeap.length==0) return false;
		else{
			int i;
			for(i = 0; i<minHeap.length; i++){
				if(minHeap[i].index == v) break;
			}
			if(i==minHeap.length) return false;
			else return true;
		}
	}
	//取出堆的root
	public static Vertex extractMin(Vertex[] V){
		if(V.length<=0) return null;
		Vertex root = V[0];
		Vertex lastNode = V[V.length-1];
		V[0] = lastNode;
		return root;
	}
	//元素i上浮
	public static void minHeapUp(Vertex[] array, int i){
		//插入的时候会用到上浮操作
		int j;
		Vertex temp = array[i];
		j = (i - 1)/2;//父节点
		while(j>=0 && i!=0){
			//因为是最小堆,所以如果父节点比子节点小,这是正常情况,
			//不需要恢复堆次序,所以直接break
			if(array[i].d<=temp.d)
				break;
			array[i] = array[j];
			i=j;
			j = (i-1)/2;
		}
		array[i] = temp;
	}
	//元素i下沉,堆中一共n个元素
	public static void minHeapDown(Vertex[] array, int i, int n){
		//删除节点会用到下移操作
		int j;
		Vertex temp = array[i];
		j = 2 * i + 1;//子节点
		while(j<n){
			if(j + 1 < n && array[j+1].d<array[j].d)
				j = j+1;//在保证有右子节点的情况下,找左右孩子中较小的
			//子节点比父节点大,则不用对父节点下移
			if(array[j].d>=temp.d)
				break;
			array[i] = array[j];
			i=j;
			j = 2*i+1;
		}
		array[i] = temp;
	}
	//建堆
	public static void makeMinHeap(Vertex[] array, int n){
		for(int i = n / 2 - 1; i >= 0; i--){
			minHeapDown(array,i,n);
		}
	}
}

结果:


对比上图

A->V0, B->V1...F->V5

点为V0,d为从起点V0到各个点的最短距离。



A*放下一篇写。。。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值