A*算法简单实现(C语言)

简单的A*算法,后续考虑更复杂情况,一个C文件即可,VS,linux等都可以直接编译运行。后面首先考虑加上图形化界面(地图可以自己输入确定也考虑下)

A*算法原理

这个很简单就不多说了,根据f = g + h。像是欧几里得距离,绝对距离等等,还有周围8节点以及4节点(16节点)对算法的影响也都是同样的道理,简单修改即可

算法流程

//算法流程
//构建地图
//把起点放入open list

//while(1) //循环结束在找到目标点(如果没有目标点怎么办)

//从open list中找到有最小的F值,作为当前点
    //如果open list为空的,查找失败,没有目标节点 

//把当前点加入到close list

//把当前点的四周节点加入到open list(需判断这个节点状态(状态包含集合),写个节点判断函数)
    //不是open list中的节点(进一步判断)
        //是目标节点
            //找到目标点,目标节点的父节点设为当前节点,循环结束
        //是不可到达节点/close list节点
            //跳过不处理
        //普通节点
            //加入open list,父节点为当前节点
    //是open list中的节点(判断G值)
        //经由当前点到达这个节点G值更小
            //更改节点的父节点为当前节点,更改F、G值,重新以F排序open list
        //不是更优
            //跳过不处理    

//通过目标节点的父节点慢慢找回起点。即为路径

A*源代码

这里面好多函数都是我需要用到然后写的简单函数,用些库函数效率会更高,不过我都是需要什么函数直接顺手写一下。
写这个时完全自己写的,没有参考网上一点,想着锻炼下自己的C语言能力吧,下一步就是和网上的代码进行对比吧,优化精简。
开始时想直接输入地图大小,障碍物距离、起始坐标、终点坐标然后直接打印路径。接下来再完善下吧


#include <stdio.h>
#include <stdlib.h>
#include <math.h>   //为了使用sqrt()函数

#define END_X 0
#define END_Y 4
#define START_X 3
#define START_Y 1
#define MAP_LENTH 5
#define MAP_WIDTH 5

#define ERROR -1

//---------------------------------------------------------------------------//

//定义open list和close list(里面有节点,节点还有自己的属性)
//链表数据区,是个结构体,不是链表。也就是节点的数据F、G、H还有坐标等等

typedef struct Open_list Open_list, * pOpen_list;
typedef struct Node
{
    //父节点
    pOpen_list  pFather;
    float G;
    float H;
    //F值
    float F;
    //x,y坐标
    int x;
    int y;
}Node, * pNode;

typedef struct Open_list
{
    struct Open_list * next;
    struct Node node; 
}Open_list, * pOpen_list;

//---------------------------------------------------------------------------//
//相关函数
float my_abs(int x);
//距离函数(给两个坐标(x1,y1)和(x2,y2))
float my_distance(int x1, int y1, int x2, int y2);
//添加链表
void list_add_tail(pOpen_list my_list, pOpen_list add_node);
//遍历链表,返回与tmpY和tmpY匹配的节点指针
pOpen_list list_browse(pOpen_list my_list, int tmpX, int tmpY);
//判断链表中是不是有某个节点(通过坐标来确定),有的话返回0,没有返回1
int judge_node_exist(pOpen_list mylist, int x, int y);
//删除链表中某个节点,通过坐标删除,并且返回这个删除的节点指针,方便加入到close list
pOpen_list list_delete_point(pOpen_list my_list, int tmpX, int tmpY);
//找到链表中最小的f值的函数,输入链表,返回最小f的节点
pOpen_list find_min_f(pOpen_list my_list);
//打印open list中的各个节点坐标以及F值
void msg_open_list(pOpen_list my_list);
//打印父节点坐标函数
void printf_father_node(pOpen_list my_list);

//---------------------------------------------------------------------------//


int main(int argc, char ** argv)
{
    //************************************************************************//
    //1、构建地图
    
    //障碍点为1,start为2,end为3   //地图太大了么
    // int map[20][20] = 
    // {
    //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 0, 0, 0, 0, 3, 0, 0},
    //     {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 1, 1, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
    //     {0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
    //     {0, 0, 2, 0, 0, 0, 0, 0, 0, 0},
    //     {1, 0, 0, 0, 0, 0, 0, 0, 0, 0}
    // };

	//障碍点为1,start为2,end为3
    int map[MAP_LENTH][MAP_WIDTH] = 
    {
        {0,0,0,0,3},
        {0,0,0,1,1},
        {1,1,1,0,0},
        {0,2,0,1,0},
        {0,0,0,0,0}
    };

    //************************************************************************//
    
    //2.把起点放入open list

    //创建一个指针pO指向open list和一个指针pC指向close list,注意现在open list是pO指向的那块内存
    pOpen_list pO = (pOpen_list)malloc(sizeof(Open_list));
    pO->next = NULL;
    //pO = NULL;  千万不可以,NULL不可以访问的,除非pO赋值
    pOpen_list pC = (pOpen_list)malloc(sizeof(Open_list));
    pC->next = NULL;

    //创建起始节点并初始化,创建一个目标节点
    pOpen_list start = (pOpen_list)malloc(sizeof(Open_list));
    start->next = NULL;
	pOpen_list end = (pOpen_list)malloc(sizeof(Open_list));
	end->next = NULL;
    
    start->node.pFather = NULL;
    start->node.x = START_X;
    start->node.y = START_Y;
    start->node.G = 0;
    start->node.H = (my_abs(END_X - START_X) + my_abs(END_Y - START_Y));
    start->node.F = start->node.G + start->node.H;

	end->node.pFather = NULL;
    end->node.x = END_X;
    end->node.y = END_Y;

    //起始节点加入到open list
    list_add_tail(pO, start);

 
    int i,j;    //计数来遍历

    //************************************************************************//
	int cir = 1;
    while(cir)
    {
        //printf("-------------------begin while--------------\n");
		//寻找最小的F值节点,记为pCurrent(第一次循环也就是起点)
        //pOpen_list pCurrent = (pOpen_list)malloc(sizeof(Open_list));
		msg_open_list(pO);	//现在open list中有的节点
		pOpen_list pCurrent = find_min_f(pO);  //有可能找不到
		
        //*********************************************************************//

		//把当前点从open list中移除(通过坐标),加入到close list,记为p
		pOpen_list p = list_delete_point(pO, pCurrent->node.x, pCurrent->node.y);
		list_add_tail(pC, p);
		printf("core is (%d, %d)\n", p->node.x, p->node.y);
		
		printf("now ,the open list is as follow\n");
		msg_open_list(pO);
		//printf("------------------------begin for  for------------------\n");

        //*********************************************************************//

        //还要考虑是不是障碍物,有没有在close list或者open list,考虑是不是边界
        //这块应该是遍历当前节点(p->node.x, p->node.y)的四周,并且都加入open list
        for(i = -1; i < 2; i++)
        {
            for(j = -1; j < 2; j++)
            {
				if((p->node.x + i < 0) || (p->node.x + i > 4) || (p->node.y + j < 0) || (p->node.y + j > 4))	//超过边界了,跳过这次循环
					continue;
				
				if(judge_node_exist(pO, (p->node.x + i), (p->node.y + j)))    //不是open list里面的节点
                {
                    if(1 == map[p->node.x + i][p->node.y + j])   //不可到达
                    {
						//printf("(%d, %d) is not-reach\n", (p->node.x + i), (p->node.y + j));
						continue;
					}						
                    else if(!(judge_node_exist(pC, (p->node.x + i), (p->node.y + j))))   //在close list中
                    {
						//printf("(%d, %d) is close list\n", (p->node.x + i), (p->node.y + j));
						continue;
					}   
                    else if(((p->node.x + i)==END_X) && ((p->node.y+j)==END_Y))  //是目标节点,初始化
                    {
                        printf("hahahahhaha,zhaodaole\n");
						end->node.pFather = p;		//当前节点设为end 的父节点
						cir = 0;	//跳出循环标志,注意跳出的是for
                        break;
                    }
                    else    //不在open list中的普通节点,加入进去初始化并设好父节点
                    {
                        //printf("(%d, %d) is normal node\n", (p->node.x + i), (p->node.y + j));
						
						pOpen_list pTmp = (pOpen_list)malloc(sizeof(Open_list));
						pTmp->next =NULL;

                        pTmp->node.pFather = p;  //父节点为当前节点
                        //节点坐标
                        pTmp->node.x = p->node.x + i;
                        pTmp->node.y = p->node.y + j;
                        //节点G.H.F值       G值怎么算好(找到父节点和现在节点的坐标了)
                        pTmp->node.G = my_distance(pTmp->node.x, pTmp->node.y, START_X, START_Y);   //初始节点到其实际距离
                        pTmp->node.H = (my_abs(END_X - pTmp->node.x) + my_abs(END_Y - pTmp->node.y));
                        pTmp->node.F = pTmp->node.G + pTmp->node.H;

                        //加入到open list
                        list_add_tail(pO, pTmp);
						msg_open_list(pO);
                    }				
                }
                else    //在open list中了
                {
					//printf("---------------------in open list-------------------\n");
					//printf("(%d, %d) in open list\n", (p->node.x + i), (p->node.y + j));
					//首先根据坐标找到他的指针
                    //pOpen_list pTmp = (pOpen_list)malloc(sizeof(Open_list));
					//pOpen_list pTmp = (pOpen_list)malloc(sizeof(Open_list));
                    pOpen_list pTmp = list_browse(pO, (p->node.x + i), (p->node.y + j));
                    //定义好两个的G值(一个是原本的G值,一个是通过当前节点到的G值)
					//所以用核心节点的G值加上当前节点到核心节点的值
                    float currentG = p->node.G + my_distance(pTmp->node.x, pTmp->node.y, p->node.x, p->node.y);
                    float pastG = pTmp->node.G;
                    
					//printf("currentG: %f      pastG: %f\n", currentG, pastG);
                    if(currentG < pastG)    //当前更优
                    {
                        pTmp->node.pFather = p; //更换父节点
                        //注意,更改F值和G值一定
                        pTmp->node.G = currentG;
                        pTmp->node.F = pTmp->node.G + pTmp->node.H;
                    }   
                }
            }
			if(cir == 0)	//跳出外层的for循环
				break;
        } 
    }
    
	
	//这里不能用!!!!msg_open_list啊,这个是打印所有的坐标卧槽了
	// msg_open_list(end->node.pFather);		//0,3
	
	// msg_open_list((end->node.pFather)->node.pFather);  //1,2
	// msg_open_list(end->node.pFather->node.pFather->node.pFather);
	printf_father_node(end);



    
    return 0;
}
//找到了,然后跳出来了循环

//尾部插入链表
void list_add_tail(pOpen_list my_list, pOpen_list add_node)
{
	pOpen_list tmp = my_list;
	while (tmp->next != NULL)
	{
		tmp = tmp->next;
	}
	tmp->next = add_node;
	add_node->next = NULL;
}

//删除链表中某个节点,通过坐标删除,并且返回这个删除的节点指针,方便free
pOpen_list list_delete_point(pOpen_list my_list, int x, int y)
{

    while (my_list->next != NULL)
	{
        if((my_list->next->node.x == x) && (my_list->next->node.y == y))  //找到删除节点
        {
            pOpen_list tmp = my_list->next;
			//这个节点不是最后一个节点
			if(my_list->next->next != NULL)
			{
				my_list->next = my_list->next->next;

				tmp->next = NULL;
			}
			else //最后一个节点
			{
				my_list->next = NULL;

				tmp->next = NULL;
			}
            return tmp;
        }
        my_list = my_list->next;  
	}

	return NULL;
}

//遍历链表,返回与p->node.y和p->node.y匹配的节点指针
pOpen_list list_browse(pOpen_list my_list, int x, int y)
{

    while (my_list->next != NULL)
	{
		if((my_list->next->node.x == x) && (my_list->next->node.y == y))
        {
            return my_list->next;
        }
        my_list = my_list->next;  
	}

	return NULL;
}

//判断链表中是不是有某个节点(通过坐标来确定),有的话返回0,没有返回1
int judge_node_exist(pOpen_list mylist, int x, int y)
{
    while(mylist->next != NULL)
    {
        if((mylist->next->node.x == x) && (mylist->next->node.y == y))	//在open list
        {
            return 0;
        }
        mylist = mylist->next;
    }

	return 1;
}

//找到链表中最小的f值的函数,输入链表,返回最小f的节点
pOpen_list find_min_f(pOpen_list my_list)
{
    //定义一个临时变量tmpf为第二个节点的F值,挨个比下去
    int tmpf = my_list->next->node.F;
    pOpen_list tmpp = my_list->next;

    while(my_list->next != NULL)
    {
        
        if(tmpf > my_list->next->node.F)
        {
            tmpf = my_list->next->node.F;
            tmpp = my_list->next;       //用一个循环就可以找到,注意!!!
        }
        my_list = my_list->next;
    }

    //找到了F值即为tmpf,怎么找到对应的节点,为什么不跟着定义一个临时节点呢
    return tmpp;
}

//打印open list中的各个节点坐标以及F值
void msg_open_list(pOpen_list my_list)
{
    while(my_list->next != NULL)
    {
        int x = my_list->next->node.x;
        int y = my_list->next->node.y;

        float f = my_list->next->node.F;
        printf("is (%d, %d).   F = %f\n", x, y, f);

        my_list = my_list->next;
    }
}

//打印父节点坐标函数
void printf_father_node(pOpen_list my_list)
{
	while(my_list->node.pFather != NULL)
	{
		printf("is(%d, %d)\n", my_list->node.pFather->node.x, my_list->node.pFather->node.y);

		my_list = my_list->node.pFather;
	}

	printf("track end\n");
}

//绝对值函数(我的)
float my_abs(int x)
{
    if(x < 0)
    {
        return (float)(-x);
    }
    else
    {
       return (float)(x);
    }
}

//距离函数(给两个坐标(x1,y1)和(x2,y2))
float my_distance(int x1, int y1, int x2, int y2)
{
    return sqrt(  (my_abs(x1-x2)*my_abs(x1-x2)) + (my_abs(y1-y2)*my_abs(y1-y2)) );
}

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值