思路大致如下:
首先有一张地图x*y的地图,为了方便,我们让每个点都用一个数字(10*x+y)来表示的话,也就是每个点唯一的属性。
1、定义一个包含点的信息的结构体,包含每个点的G值,F值,x,y坐标,它的父节点的x,y坐标。并定义此结构体型变量的数组,以存储路径。因为每个结构替里都包含了节点和它的父节点,因此我们可以用递归来输出路径。
2、定义一个链表,主要储存开启列表,关闭列表中的点的信息。之所以是链表,是为了对F值进行动态排列,每增加一个F值,我们都可以按一定顺序加入列表中。这样以来,每次的查找都会很方便。
3、此外,我们还需要设计一个将某点插入链表的函数,因为我们要很频繁的变换点的归属表。
4、删除链表某一节点的函数,有插入就有删除,当然,还有创建。
5、点的归属表的判断
6、更新表的F值,因为我们要不断做出比较,路径节点不断移动,于是随着G值的改变,F值也会随之改变。
7、判断方向的可行性
8、求出可行方向的点坐标,即下面经常讲到的“新点”。
9、最后写A星主程序
开启列表=关闭列表=NULL——创建开启列表,head节点就是路径的起点,也就是第一个父节点
循环:把父节点从开启列表中删除——判断是否到达终点——是,break,跳出while
——否——从右,下,左,上四个方向判断哪个方向可通过——得到一个可通过方向——计算一个新节点——判断在哪个列表中,可以用flag来标记
——都不在,则加入开启列表
——在开启列表,比较他们的F值,更新
——在关闭列表,比较他们的F值,更新,转移到开启列表
——将父节点加入到关闭列表中——判断开启列表是否为空——否,则将刚刚的新节点又作为父节点赋值给起点坐标——回到循环开头
对应如下:
1、节点属性Of结构体
struct dian
{
int
ng;//G值
int nf;
//F值
int myx;
int
myy;//自己
int
fatherx;
int
fathery;//父节点
};
路径的输出:
void putout()
{
int nxx =
spaydian[nend].nfatherx;
int
nyy = spaydian[nend].nfathery;
digui(nxx,nyy)
;
}
void digui(int nxx,int nyy)
{ map[nxx][nyy]
= '通过标志‘;
if( nxx != 0 || nyy != 0 )
{
int nxxyy = 10*nxx+nyy;
nxx = dian[nxxyy].nfatherx;
nyy = dian[nxxyy].nfathery;
digui(nxx,nyy);
}
}
2、链表
typedef struct list {
int n_f;
int n_x;
int n_y;
int nfather_x;
int nfather_y;
struct list *next;
};
3、将dian(属性值)插入开启或关闭链表中
list * insert(list *head,int dian)
{
list
*p0,*p1,*p2;
p1=head;
p0=(list*)malloc(sizeof(list));//以下的将数据逐一赋值
p0->n_f = dian[dian].nf;
p0->n_x = dian[dian].myx;
p0->n_y = dian[dian].myy;
p0->nfather_x = dian[dian].fatherx;
p0->nfather_x = dian[dian].fathery;
p0->next = NULL;
if(head==NULL)
{
head=p0;
p0->next=NULL;
}
else
{
while((p0->n_f >
p1->n_f)&&(p1->next!=NULL))//查找临界点
{
p2=p1;
p1=p1->next;
}
if(p0->n_f <=
p1->n_f)//将dian插入合适的位置(F值排列)
{
if(head==p1)
head=p0;
else
p2->next=p0;
p0->next=p1;
}
else
{
p1->next=p0;
p0->next=NULL;
}
}
return (head);
}
4、将某点从链表中删除
list *del(list *head,int num_x,int num_y)
{
list *p1,
*p2;
if(head ==
NULL)
{
printf("\nlist null!\n");
return (head);
}
p1 =
head;
while((num_y
!= p1->n_y ||num_x != p1->n_x
)&& p1->next !=
NULL)
{
p2=p1;
p1=p1->next;
}
if(num_x ==
p1->n_x && num_y ==
p1->n_y )
{
if(p1==head)
head=p1->next;
else
p2->next=p1->next;
}
return
(head);
}
list *creat(void)
{
list *head;
list *p1;
int n=0;
p1=(list*)malloc(sizeof(list));
p1->n_f =
18;
p1->n_x =
0;
p1->n_y =
0;
p1->nfather_x =
-1;
p1->nfather_x =
-1;
p1->next =
NULL;
head = NULL;
head=p1;
return(head);
}
5、判断是否在开启/关闭列表里,返回1在,0则不在
int isinopenlist/isincloselist(int nx,int
ny)(两个函数)
{
list *p1,
*p2;
p1 =
openlist;
while((ny !=
p1->n_y || nx != p1->n_x
)&& p1->next !=
NULL)
{
p2 = p1;
p1 = p1->next;
}
if(nx == p1->n_x &&
ny == p1->n_y)
return 1;
else
return 0;
}
6、更新链表中某点的F值
list * tonewlist(list *head,int dian)
{
list *p1,
*p2;
p1=head;
while(dian[dian].myx!=p1->n_x
&&
dian[dian].myy!=p1->n_y
&&
p1->next!=NULL)
{
p2=p1;
p1=p1->next;
}
if(dian[dian].myx==p1->n_x
&&
dian[dian].myy==p1->n_y)
p1->n_f = dian[dian].nf;
return (head);
}
7、判断i(1右,2下,3左,4上)方向是否可走,返回1为可走,0为不可走
int judge(int nx,int ny,int i)
if
(i==1) //判断向右可否行走
{
if (map[nx][ny+1]==可过 &&
ny
return 1;
else
return 0;
}
else if
(i==2) //判断向下可否行走
{
if (map[nx+1][ny]==可过 &&
nx
return 1;
else
return 0;
}
else if
(i==3)//判断向左可否行走
{
if (ny
>0&&map[nx][ny-1]==可过)
return 1;
else
return 0;
}
else if
(i==4)//判断向上可否行走
{
if (nx >
0&&map[nx-1][ny]==可过)
return 1;
else
return 0;
}
else
return 0;
8、求往i方向上的新的x\y点(两个函数)
int newx/y(int nx/ny,int i)
{
if(i ==
1)//右
nx = nx;/ny = ny+1;
else if(i ==
2)//下
nx = nx+1;/ny = ny;
else if(i ==
3)//左
nx = nx;/ny = ny-1;
else if(i ==
4)//上
nx = nx-1;/ny = ny;
return
nx/ny;
}
接下来,就是写主要的A*了:
void astar()
{
openlist=closelist=NULL;
openlist=creat();
while(openlist!=NULL)
{
openlist=del(openlist,xstart,ystart);//将父节点从开启列表中删除
if(xstart==xend&&ystart==yend)
{
map[9][9]
= '通过标志';
break;
}
for(int
i=1;i<=4;i++)//逐个判断4个方向。1右,2下,3左,4上
{ if(judge(xstart , ystart ,i ))
{
xnewstar=newx(xstart , i);
ynewstar=newy(ystart , i);
if(openlist!=NULL)
flagofopen=isinopenlist(xnewstar,ynewstar);
else
flagofopen=0;
if(openlist!=NULL)
flagofclose=isincloselist(xnewstar,ynewstar);
else
flagofclose=0;
dian=10*xnewstar+ynewstar;//新点属性
dian[dian].ng=dian[10*xstart+ystart].ng+1;//新点G值
nh=(nend-dian)/10+(nend-dian)%10;//新点H值
if(flagofopen==0&&flagofclose==0)//新点都不在列表中
{
dian[dian].nf=dian[dian].ng+nh;
dian[dian].nfatherx=xstart;
dian[dian].nfathery=ystart;
dian[dian].myx=xnewstart;
dian[dian].myy=ynewstary;
openlist=insret(openlist,dian);//新点插入开启列表
}
else if(flagofopen==1)//若已经在开启列表,则比较他们的F值,更新
{
if(dian[dian].nf>dian[dian].ng+nh&&dian[dian].nf!=最大的的F值)
{
dian[dian].nf=dian[dian].ng+nh;
openlist=tonewlist(openlist,dian);//更新dian的F值
}
}
else if(flagofclose==1)//若已经在关闭列表,则比较他们的F值,更新,转移列表
{
if(dian[dian].nf>dian[dian].ng+nh&&dian[dian].nf!=最大的的F值)
{
dian[dian].nf=dian[dian].ng+nh;
closelist=tonewlist(closelist,dian);//更新新点的F值
closelist=del(closelist,xnewstart,ynewstart);//从关闭列表中删除
openlist=insert(openlist,dian);//加入开启列表中
}