由于学校的机器学习课程马上要讲搜索,涉及到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*放下一篇写。。。