A*(A-Star)算法是一种静态路网中求解最短路最有效的方法,是启发搜索中的一种。
1.起源
名字创意来源于第一届百度之星比赛决赛中有题目是一道经典的8数码题目,解这道题,冠军ACRush使用了A*算法(Astar)。Astar又包含了“百度之星”的含义。
2.算法的描述
2.1 该算法可以用如下等式表示:
f(n) = g(n) + h(n)
其中
f(n) 是从初始点经由节点n到目标点的移动耗费。
g(n) 是在状态空间中从起点到n节点的实际耗费。
h(n) 从网格上那个方格移动到终点的预估移动耗费。这经常被称为启发式的,这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长度,因为路上可能存在各种障碍(墙,水,等等)。
g(n) 是在状态空间中从起点到n节点的实际耗费。
h(n) 从网格上那个方格移动到终点的预估移动耗费。这经常被称为启发式的,这样叫的原因是因为它只是个猜测。我们没办法事先知道路径的长度,因为路上可能存在各种障碍(墙,水,等等)。
2.2 该算法的伪代码描述如下:
创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点;
算起点的估价值;
将起点放入OPEN表;
while(OPEN!=NULL)
{
从OPEN表中取估价值f最小的节点n;
if(n节点==目标节点)
{
break;
}
for(当前节点n 的每个子节点X)
{
算X的估价值;
if(X in OPEN)
{
if( X的估价值小于OPEN表的估价值 )
{
把n设置为X的父亲;
更新OPEN表中的估价值; /*取最小路径的估价值*/
}
}
if(X in CLOSE)
{
continue;
}
if(X not in both)
{
把n设置为X的父亲;
求X的估价值;
并将X插入OPEN表中;
}
}/* end for */
将n节点插入CLOSE表中;
按照估价值将OPEN表中的节点排序; /*实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。*/
}/* end while(OPEN!=NULL) */
保存路径,即 从终点开始,每个节点沿着父节点移动直至起点,这就是你的路径;
3.算法实现的几个关键点
3.1 维护open表
算法中从open表中需要选择估算值最小的节点进行,相当于open表是一个有序序列。对于open表的实现可以用完全排序链表。但是对于节点数量大的网络中有一个更优的选择,那就是二叉堆。
二叉堆是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。 在这里用到是最小二叉堆。二叉堆的介绍也可以参考: Using Binary Heaps in A* Pathfinding
二叉堆是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。二叉堆有两种:最大堆和最小堆。最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。 在这里用到是最小二叉堆。二叉堆的介绍也可以参考: Using Binary Heaps in A* Pathfinding
二叉堆实现代码如下:
bheap.h
/*
* author: Atom
* date: 2012/12/02
* file: bheap.h
*/
#ifndef _BHEAP_H
#define _BHEAP_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define DEF_SIZE 16 /* 默认heap大小 */
#define INC_RATE 1.5 /* heap自动增长速度 */
enum Bheap_type
{
BHEAP_TYPE_BIG, /* 大根二叉堆 */
BHEAP_TYPE_SMALL /* 小根二叉堆 */
};
struct Bheap_node
{
void* value;
};
struct Bheap
{
struct Bheap_node** head;
size_t size; /* 当前元素个数 */
size_t max_size; /* 堆容量 */
enum Bheap_type Btype; /* 堆类型 */
};
/* condition result
* n1 > n2 1
* n1 == n2 0
* n1 < n2 -1
*/
typedef int (*Bheap_compare_t)(struct Bheap_node* n1, struct Bheap_node* n2);
/* condition result
* n1 == n2 1
* n1 != n2 0
*/
typedef int (*Bheap_equal_t)(struct Bheap_node* n1, struct Bheap_node* n2);
/* free 节点,包括bn->value */
typedef void (*Bheap_free_node_t)(struct Bheap_node* bn);
/*根据 Bheap_type 创建二叉堆,如果未指定堆大小或指定的堆大小 小于等于 1,则初始化堆容量为16*/
static struct Bheap* Bheap_create(size_t size, enum Bheap_type Btype)
{
struct Bheap* heap = NULL;
if (NULL == (heap = (struct Bheap*)malloc(sizeof(struct Bheap))))
{
fprintf(stderr, "Bheap malloc error!\n");
return (NULL);
}
memset(heap, 0x00, sizeof(struct Bheap));
if (size > 1)
{
heap->head = (struct Bheap_node**)malloc(sizeof(struct Bheap_node*) * (size + 1));
if (NULL == heap->head)
{
fprintf(stderr, "Bheap.head malloc error!\n");
return (NULL);
}
heap->max_size = size;
}
else
{
heap->head = (struct Bheap_node**)malloc(sizeof(struct Bheap_node*) * ((DEF_SIZE) + 1));
if (NULL == heap->head)
{
fprintf(stderr, "Bheap.head malloc error!\n");
return (NULL);
}
heap->max_size = DEF_SIZE;
}
heap->Btype = Btype;
return (heap);
}
/* 初始化二叉堆 */
static void Bheap_init(struct Bheap* heap)
{
if (NULL == heap)
return;
memset(heap->head, 0x00, (sizeof(struct Bheap_node*) * (heap->size + 1)));
heap->size = 0;
}
/* 增长二叉堆容量 */
static size_t __inc_Bheap_size(struct Bheap* heap)
{
if (NULL == heap)
return (-1);
heap->max_size = (size_t)((INC_RATE) * heap->max_size);
heap->head = (struct Bheap_node**)realloc(heap->head, sizeof(struct Bheap_node*) * (heap->max_size + 1));
if (NULL == heap->head)
return (-1);
return (0);
}
/* 交换节点内容 */
static void __swap_Bheap_node(struct Bheap_node** n1, struct Bheap_node** n2)
{
struct Bheap_node* temp;
if (*n1 == *n2)
return;
temp = *n1;
*n1 = *n2;
*n2 = temp;
}
/* 入堆操作 */
static int Bheap_push(struct Bheap* heap, struct Bheap_node* node, Bheap_compare_t comp)
{
int n = 0, c;
if ((NULL == heap) || (NULL == node) || (NULL == comp))
return (-1);
if ((heap->size == heap->max_size) && (-1 == __inc_Bheap_size(heap)))
{
fprintf(stderr, "increase heap size error!\n");
return (-1);
}
heap->size++;
n = heap->size;
heap->head[heap->size] = node;
/* 冗余代码,减少循环中的判断 */
if (BHEAP_TYPE_SMALL == heap->Btype)
{
/* 小根堆 */
for ( ; ; )
{
if (1 == n)
break;
c = n;
n = n>>1;
if (1 == comp(heap->head[n], heap->head[c]))
__swap_Bheap_node(&(heap->head[n]), &(heap->head[c]));
else
break;
}
}
else
{
/* 大根堆 */
for ( ; ; )
{
if (1 == n)
break;
c = n;
n = n>>1;
if (-1 == comp(heap->head[n], heap->head[c]))
__swap_Bheap_node(&(heap->head[n]), &(heap->head[c]));
else
break;
}
}
return (0);
}
/* 移除节点 */
static struct Bheap_node* Bheap_remove(struct Bheap* heap, size_t idx, Bheap_compare_t comp)
{
int n = 0, c;
if ((NULL == heap) || (idx < 1) || (idx > heap->size) || (NULL == comp))
return (NULL);
__swap_Bheap_node(&(heap->head[idx]), &(heap->head[heap->size]));
n = idx;
heap->size--;
/* 冗余代码,减少循环中的判断 */
if (BHEAP_TYPE_SMALL == heap->Btype)
{
/* 小根堆 */
for ( ; ; )
{
c = n;
n = n<<1;
if (n > heap->size)
break;
if ((n + 1) > heap->size)
{
if (1 == comp(heap->head[c], heap->head[n]))
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n]));
else
break;
}
else
{
if (1 == comp(heap->head[n], heap->head[n + 1]))
{
if (1 == comp(heap->head[c], heap->head[n + 1]))
{
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n + 1]));
n += 1;
}
else
break;
}
else
{
if (1 == comp(heap->head[c], heap->head[n]))
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n]));
else
break;
}
}
}/* end for */
}
else
{
/* 大根堆 */
for ( ; ; )
{
c = n;
n = n<<1;
if (n > heap->size)
break;
if ((n + 1) > heap->size)
{
if (-1 == comp(heap->head[c], heap->head[n]))
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n]));
else
break;
}
else
{
if (-1 == comp(heap->head[n], heap->head[n + 1]))
{
if (-1 == comp(heap->head[c], heap->head[n + 1]))
{
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n + 1]));
n += 1;
}
else
break;
}
else
{
if (-1 == comp(heap->head[c], heap->head[n]))
__swap_Bheap_node(&(heap->head[c]), &(heap->head[n]));
else
break;
}
}
}/* end for */
}
return (heap->head[heap->size + 1]);
}
/* 出堆操作 */
static struct Bheap_node* Bheap_pop(st