【数据结构与算法】A*算法——自动寻路

news2024/11/26 2:37:10

这里写目录标题

  • 一.为什么用A*算法
  • 二.A*算法的实现原理
  • 三.A*算法的实现
    • 1.初始化地图
    • 2.格子初始化
    • 3.两个列表
    • 4.起点到终点的路径
    • 5.起点到终点的最佳路径★
    • 6.资源的释放
  • 四.完整代码
    • 1.Astar.h
    • 2.Astar.cpp
    • 3.main.cpp
    • 4.运行结果

一.为什么用A*算法

上节课我们已经讲了最短路径算法,但是我们为什么还要使用A*算法呢?
那就要讲讲最短路径算法的缺点了.
很明显,我们要得到最短的,那么就需要比较,就需要我知道所有的路径,然后比较出最短的,那么我需要知道所有的路径这是非常庞杂的工作.

A*算法就巧妙的运用了人的思想,类似于人工智能一样,不需要事先知道所有的路径,只需要每一步都是朝向目的地的,同时附近可以走一步的.
虽然得出的未必是最短的一条路径,但是绝对是相对近的一条路.

二.A*算法的实现原理

那么我们需要注意的点就可以分为离终点有多远,附近的下一步能不能走通.
一般地图我们都可以当做二维数组来进行处理.
我们直接看图说话:
如果12是我们的起点,16是我们姚到的目的地,那我们是如何操作的呢?
看下面的步骤吧!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
哎呀妈呀,可算是画完了.
这就是A*算法,接下来我们来代码实现吧.

三.A*算法的实现

头文件,我们先来看看需要做哪些事?

这是设置了从起点到改点的步数权重,就是G.
刚刚我是用的1,我们可以用10,都是一样的.
在这里插入图片描述
每个格子的属性,因为我们用的是二维数组表示地图,那么x,y代表数组下标位置.
G是起点格子到该格子的步数,H是该格子到终点格子的步数.
我们以计算总的步数F来判断下一步该走哪一个格子,也就是F最小的.
parent就是链接路径,
在这里插入图片描述
格子我们动态分配内存,同时最后我们需要清理.
当然要初始化地图和寻找路线.
在这里插入图片描述

1.初始化地图

二维数组的基本信息保存,其他函数需要用.
在这里插入图片描述
在这里插入图片描述

2.格子初始化

在这里插入图片描述

3.两个列表

在这里插入图片描述

4.起点到终点的路径

因为我们最后找到的节点是终点,但是我们需要的路径是从起点到终点,所以我们需要正序的将格子放入list容器中.
在这里插入图片描述

5.起点到终点的最佳路径★

首先将起点加入到openList.
在这里插入图片描述
如果openList不为空就一直找终点.最后为空就说明没有找到.
在这里插入图片描述
下面的都是在上面这个循环里面的.

先找到openList里面最小的F的格子.
在这里插入图片描述
对容器的遍历用迭代器
在这里插入图片描述
当这个格子判断后,我们就可以放入到closeList了,同时在openList里面排除.
在这里插入图片描述

然后我们就可以判断这个节点周围的点是否可以走通.
在这里插入图片描述
用vector容器来装可以走通的格子.
因为我们需要的这个格子周围的点,我们可以用两个for循环就包含了9个格子,但是下面我们会根据条件排除一些点.

在这里插入图片描述
如果越界了不可以,因为是一个二维数组,如果是障碍物不可以,这里我们在二维数组中设置的值为1的是障碍物.,如果在closeList的也不可以,因为我们已经走过了.
同时我们只需要上下左右就可以了.
在这里插入图片描述
判断某个格子是否在某个列表.
在这里插入图片描述
这个时候我们就得到了周围可走通格子的容器了,我们进行遍历计算其F值.

在这里插入图片描述
对于周围每个可以走通的格子我们进行判断是否在openList里面,如果没有就加入,有的话,就判断需不需要进行更新.判断的依据就是G是否更小,因为H都是一样的.

在这里插入图片描述
距离起点的步数计算.就是当前各个到下一步的步数+其父亲节点的G步数.
在这里插入图片描述
H(点到终点的距离)计算用了勾股定理.
如2~16的距离.
在这里插入图片描述

在这里插入图片描述
F的计算自不必多说.
在这里插入图片描述

遍历完周围的格子后,就清空,下一次又来用,并判断终点的格子有没有加入到openList.
如果有,就说明找到了,直接返回这个终点.
如果最后openList都为空了,那么就是没有找到终点,返回空.
在这里插入图片描述

6.资源的释放

将两个列表的格子释放内存.
在这里插入图片描述

四.完整代码

1.Astar.h

#pragma once
#include <list>

const int kCost1 = 10;
const int kCost2 = 14;

typedef struct _Point
{
	int x,y;//二维数组下标
	int F, G, H;//F=G+H
	struct _Point* parent;//parent的坐标
}Point;

//分配一个节点(格子)
Point* AllocPoint(int x, int y);

//初始化地图
void initAstarMaze(int* _maze, int _lines, int _colims);

//通过A*算法寻找路径
std::list<Point*>GetPath(Point* statPoint, Point* endPoint);

//清理资源,结束后必须调用
void ClearAstarMaze();

2.Astar.cpp

#include <math.h>
#include "Astar.h"
#include <iostream>
#include <vector>

static int* maze;//迷宫对应的二维数组,使用一级指针表示
static int cols;//二维数组对于的列数
static int lines;//二维数组对于的行数

static std::list<Point*>openList;//开放列表
static std::list<Point*>closeList;//关闭列表

//搜索从起点到终点的最佳路径
static Point* findPath(Point* startPoint, Point* endPoint);

//从开启列表中返回F值最小的节点
static Point* getLeasFpoint();

//获取当前点周围可达的节点
static std::vector<Point*>getSurroundPoints(const Point* point);

//判断某点是否可以用于下一步判断
static bool isCanreach(const Point* point, const Point* target);

//判断开放/关闭列表中是否包含某点
static Point* isInList(const std::list<Point*>& list, const Point* point);

//计算FGH值
static int calcG(Point* temp_start, Point* point);
static int calcH(Point* point, Point* end);
static int calcF(Point* point);

//分配一个节点(格子)
Point* AllocPoint(int x, int y)
{
	Point* temp = new Point;
	memset(temp, 0, sizeof(Point));//初始值清零
	temp->x = x;
	temp->y = y;
	return temp;
}


//初始化地图
void initAstarMaze(int* _maze, int _lines, int _colums)
{
	maze = _maze;
	lines = _lines;
	cols = _colums;
}

//通过A*算法寻找路径
std::list<Point*>GetPath(Point* startPoint, Point* endPoint)
{
	Point* result = findPath(startPoint, endPoint);

	std::list<Point*>path;
	//返回路径,如果没有找到路径,返回空链表
	while (result)
	{
		path.push_front(result);
		result = result->parent;
	}

	return path;
}


//搜索从起点到终点的最佳路径
static Point* findPath(Point* startPoint, Point* endPoint)
{
	openList.push_back(AllocPoint(startPoint->x,startPoint->y));
	while (!openList.empty())
	{
		//第一步,从开放列表中取最小的值
		Point* curPoint = getLeasFpoint();//找到F值最小的点

		//第二步,把当前节点放到关闭列表中
		openList.remove(curPoint);
		closeList.push_back(curPoint);

		//第三步,找到当前节点周围可达的节点
		std::vector<Point*>surroundPoints = getSurroundPoints(curPoint);
		std::vector<Point*>::const_iterator iter;
		for (iter = surroundPoints.begin(); iter != surroundPoints.end(); iter++)
		{
			Point* target = *iter;
			//对某个格子,如果不在开放列表中,就加入,设置当前格为其父节点.
			Point* exist = isInList(openList, target);
			if (!exist)
			{
				target->parent = curPoint;

				target->G = calcG(curPoint, target);
				target->H = calcH(target, endPoint);
				target->F = calcF(target);

				openList.push_back(target); 
			}
			else
			{
				int tempG = calcG(curPoint, target);
				if (tempG < target->G)
				{
					exist->parent = curPoint;
					exist->G = tempG;
					exist->F = calcF(target);
				}
				delete target;//如果已经存在了就不用了释放.
			}
		}

		surroundPoints.clear();
		Point* resPoint = isInList(openList, endPoint);
		if (resPoint)
		{
			return resPoint;
		}
	}
	return NULL;
}


//从开启列表中返回F值最小的节点
static Point* getLeasFpoint()
{
	if (!openList.empty())
	{
		Point* resPoint = openList.front();
		std::list<Point*>::const_iterator itor;
		for (itor = openList.begin(); itor != openList.end(); itor++)
		{
			if ((*itor)->F < resPoint->F)
			{
				resPoint = *itor;
			}
		}
		return resPoint;
	}
	return NULL;
}

//获取当前点周围可达的节点
static std::vector<Point*>getSurroundPoints(const Point* point)
{
	std::vector<Point*>surroundPoints;

	for (int x = point->x - 1; x <= point->x + 1; x++)
	{
		for (int y = point->y - 1; y <= point->y + 1; y++)
		{
			Point* temp = AllocPoint(x, y);
			if (isCanreach(point, temp))
			{
				surroundPoints.push_back(temp);
			}
			else
			{
				delete temp;
			}
		}
	}
	return surroundPoints;	
}

static bool isCanreach(const Point* point, const Point* target)
{
	if (target->x<0 || target->x>(lines - 1)
		|| target->y<0 || target->y>(cols - 1)
		|| maze[target->x * cols + target->y] == 1
		|| (target->x == point->x && target->y == point->y)
		|| isInList(closeList, target))
	{
		return false;
	}

	if (abs(point->x - target->x) + abs(point->y - target->y) == 1)//abs绝对值.上下左右
	{
		return true;
	}
	else
	{
		return false;
	}
		
}

//判断开启/关闭列表中是否包含某点
static Point* isInList(const std::list<Point*>& list, const Point* point)
{
	std::list<Point*>::const_iterator itor;
	for (itor = list.begin(); itor != list.end(); itor++)
	{
		if ((*itor)->x == point->x && (*itor)->y == point->y)
		{
			return *itor;
		}
	}
	return NULL;
}

static int calcG(Point* temp_start, Point* point)
{
	int extraG = (abs(point->x - temp_start->x) + abs(point->y - temp_start->y)) == 1 ? kCost1 : kCost2;
	int parentG = (point->parent == NULL ? NULL : point->parent->G);
	return extraG + parentG;
}
static int calcH(Point* point, Point* end)
{
	return (int)sqrt((double)(end->x - point->x) * (double)(end->x - point->x) + (double)(end->y - point->y) * (double)(end->y - point->y));
}
static int calcF(Point* point)
{
	return point->G + point->H;
}

//清理资源,结束后必须调用
void ClearAstarMaze()
{
	maze = NULL;
	lines = 0;
	cols = 0;
	std::list <Point*>::iterator itor;
	for (itor = openList.begin(); itor != openList.end();)
	{
		delete* itor;
		itor = openList.erase(itor);//获取到下一个节点
	}
	for (itor = closeList.begin(); itor != closeList.end();)
	{
		delete* itor;
		itor = closeList.erase(itor);//获取到下一个节点
	}
}

3.main.cpp

#include "Astar.h"
#include <list>
#include <iostream>
#include <Windows.h>

using namespace std;


int map[5][8] =
{
	{1,1,1,0,0,0,1,1},
	{0,0,0,0,1,0,0,0},
	{0,0,0,0,1,0,0,0},
	{0,0,0,0,1,0,0,0},
	{1,1,1,0,0,0,1,1},
};

void AStarTest()
{
	initAstarMaze(&map[0][0], 5, 8);
	//设置起始和结束点
	Point* start = AllocPoint(2, 1);
	Point* end = AllocPoint(2, 6);

	//通过A*算法寻找路径
	list<Point*>path=GetPath(start, end);

	cout << "寻路结果:" << endl;
	list<Point*>::const_iterator iter;
	for (iter = path.begin(); iter != path.end();iter++)
	{
		Point* cur = *iter;
		cout << '(' << cur->x << ',' << cur->y << ')' << endl;

		Sleep(800);
	}

	ClearAstarMaze();
}

int main()
{
	AStarTest();
	system("pause");
	return 0;
}

4.运行结果

在这里插入图片描述
就是走的这条路.
在这里插入图片描述

完结撒花
2024年8月16日17:29:59

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2045407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Qt】内置对话框

一.Qt内置对话框 Qt 提供了多种可复⽤的对话框类型&#xff0c;即 Qt 标准对话框。Qt标准对话框全部继承于QDialog类。常⽤标准对话框如下&#xff1a; 二.内置对话框分类 1.消息对话框 QMessageBox 1.1 概念 消息对话框是应⽤程序中最常⽤的界⾯元素。消息对话框主要⽤于为…

SpringBoot集成微信小程序(二)【登录、获取头像昵称及手机号】

一、背景 小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识&#xff0c;快速建立小程序内的用户体系。 微信小程序官方文档&#xff1a;https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html 二、技术栈 SpringBoot…

RFID在晶圆搬运机中的智能化实践

RFID在晶圆搬运机中的智能化实践 应用背景 在半导体制造行业&#xff0c;晶圆搬运是一个至关重要的环节&#xff0c;它不仅影响生产效率&#xff0c;还直接关系到产品的质量和成本。在如今的多品种少量生产中&#xff0c;要保障生产效率和品质&#xff0c;工序管理至关重要。…

Python环境安装及PIP安装(Mac OS版)

官网 https://www.python.org/downloads/ 安装python python-3.12.1-macos11.pkg下载后&#xff0c;安装一直下一步即可 验证是否安装成功&#xff0c;执行python3命令和pip3命令 配置环境变量 获取python3安装位置并配置在.bash_profile #查看python路径 which python3#…

卫星图像检测,分割,跟踪,超分辨率,数据集调研

卫星图像检测&#xff0c;分割&#xff0c;跟踪&#xff0c;超分辨率&#xff0c;数据集调研 超分辨率Image super-resolution: A comprehensive review, recent trends, challenges and applicationsA Review of GAN-Based Super-Resolution Reconstruction for Optical Remot…

ppt模板免费网站有哪些?自动美化工具推荐

新的8月&#xff0c;是时候以全新面貌迎接高效办公挑战了&#xff01; 想要你的PPT演示脱颖而出&#xff0c;却苦于找不到精美又免费的模板&#xff1f; 别担心&#xff0c;今天我来告诉你们&#xff1a;哪个软件有精美免费ppt模板&#xff1f; 今天我为你们精心汇总了6款PPT…

二维高斯拟合20240815

二维高斯拟合 高斯函数表达式 二维高斯函数是一个在二维空间中用来表示高斯分布的函数&#xff0c;常用于统计学、图像处理和机器学习等领域。其数学表达式通常为&#xff1a; f ( x , y ) 1 2 π σ x σ y 1 − ρ 2 exp ⁡ ( − 1 2 ( 1 − ρ 2 ) ( ( x − μ x ) 2 σ …

面向未来的算力网络连接技术架构

面向未来的算力网络连接发展特点与实践 物理层网络基础设施是算力网络的“最后一公里”&#xff0c;光纤光缆等物理层网络基础设施的发展有助于保障算力网络基础设施的稳健发展。物理层核心技术发生了巨大变化&#xff0c;在交换、传输等方面不断更新迭代&#xff0c;当前铜缆的…

【云原生】看一眼haproxy,秒变技术大佬(理论篇)

一、负载均衡 1、什么是负载均衡&#xff1f; 负载均衡&#xff1a;Load Balance&#xff0c;简称LB&#xff0c;是一种服务或基于硬件设备等实现的高可用反向代理技术&#xff0c;负载均衡将特定的业务(网络服务、网络流量等)分担给指定的一个或多个后端特定的服务器或设备&…

Secure CRT 9.x版本高亮着色配置文件

Secure CRT的网络配置文件高亮显示&#xff0c;还在完善&#xff0c;逐渐适配不同厂商 设备名字自动蓝色高亮显示设备接口名高亮显示IPv4地址、IPv6地址、MAC地址高亮显示掩码、反掩码高亮显示设备SN号高亮显示接口状态、设备状态等高亮显示各路由协议高亮显示 【下载地址】效果…

基于Spring Boot的疗养院管理系统的设计与实现

TOC springboot234基于Spring Boot的疗养院管理系统的设计与实现 第1章 绪论 1.1选题动因 当前的网络技术&#xff0c;软件技术等都具备成熟的理论基础&#xff0c;市场上也出现各种技术开发的软件&#xff0c;这些软件都被用于各个领域&#xff0c;包括生活和工作的领域。…

输出Docker容器的启动命令行脚本

当Docker容器启动后&#xff0c;如果忘记启动参数&#xff0c;比如目录挂载、端口映射等&#xff0c;可以通过Portainer等容器管理工具查看。但是&#xff0c;有时希望能获取容器启动的命令行&#xff0c;因为需要再启动一个类似容器&#xff0c;怎么办呢&#xff1f; 有一款工…

【Python养成】:输出两个等腰三角形且平行方向

项目场景&#xff1a; 遇到一个小小的高中小程序题目&#xff0c;有趣就做 了一下&#xff0c;供大家参考。 问题描述 要求使用python编程&#xff0c;实现如下图形。 原因分析&#xff1a; 其实就是 找规律和找空格。先左边后右边。具体解释可以找AI。太简单了没必要亲自讲解…

Linux 7 x86平台上安装达梦8数据库

1、环境描述 2、安装前准备 2.1 操作系统信息调研 Linux平台需要通过命令查看操作系统版本、位数、磁盘空间、内存等信息。 CPU信息 [rootray1 ~]# cat /proc/cpuinfo | grep -E "physical id|core id|cpu cores|siblings|cpu MHz|model name|cache size"|tail -n…

【PY模型训练】最终测试

Anacoda3 安装 1. conda -V 2.conda config --add channels https://mirrors.ustc.edu.cn/anaconda/pkgs/main/ 3.打开.condarc文件,将下面内容直接放入.condarc文件里面 channels: - conda-forge - defaults show_channel_urls: true default_ch…

【Qt】 QWidget的cursor属性

QWidget的cursor属性 cursor属性可以设置当鼠标指向不同控件&#xff0c;不同窗口会产生不同的效果。 API说明 cursor() 获取到当前 widget 的 cursor 属性, 返回 QCursor 对象. 当⿏标悬停在该 widget 上时, 就会显⽰出对应的形状. setCursor(const QCursor& cursor) …

CSS——less

一、less简介 1、less是一门css的预处理语言。是一个css的增强版&#xff0c;通过less可以编写更少的代码实现更强大的样式。 2、css原先也只支持变量的设置。 变量的设置可以便捷更改一样的样式&#xff0c;其中&#xff0c;calc&#xff08;&#xff09;是计算函数。 3、在le…

【6大设计原则】精通设计模式之里氏代换原则:从理论到实践,掌握代码演化的黄金法则

一、引言 1.1 设计模式的必要性 在软件开发的复杂性面前&#xff0c;设计模式提供了一套成熟的解决方案&#xff0c;它们是经过多年实践总结出来的&#xff0c;能够帮助我们应对各种编程难题。设计模式不仅仅是一种编程技巧&#xff0c;更是一种编程哲学&#xff0c;它能够提…

CSS——动画(animation)

一、过渡&#xff08;transition&#xff09; 1、通过过渡可以指定一个属性发生变化时的切换方式 2、通过过渡可以创建一些非常好的效果&#xff0c;提升用户体验 3、常用属性值 ① transition-property 指定要执行过渡的属性 多个属性间使用&#xff0c;隔开如果所有…