AStar算法基础学习总结

代码git:

https://github.com/SYRollingStone/SiYangUnityAlgorithmsSamples

一、总结

AStar是一个评估每个节点代价获取最短路径的算法。代价F = G + H.G是从起点到当前节点的真实距离,H是当前节点到终点的启发式距离。

有2个关键数据结构,一个是开放列表,一个是关闭列表。关闭列表表示这个节点已经被选为 current 并扩展过(canonical A* 做法),一般就不再考虑它。开放列表存放还已发现但尚未被弹出并扩展的节点集合。每轮从 开放列表中取出 f 最小的 current,立刻移入 关闭列表,然后扩展它的邻居。

当启发函数 一致/可一致(consistent) 时,比如我这个简单例子是四向网格+曼哈顿就是,节点进 关闭列表后不需要 reopen;否则某些变体需要允许从 关闭列表 重新回到 开放列表。

有一个关键的判断,当前最小F值节点,如果他找到一个不在关闭列表的邻居,并且这个邻居的G值大于我当前最小F节点+一次移动代价,那么就说明当前路线到达邻居节点比以往的路线都要短,那么就更新邻居节点的GHF和父亲节点信息,此时邻居节点可能还不在 开放列表里,这时第一次发现它也会更新。

二、一个四向简单矩阵的的AStar代码

这是最简单的AStar算法演示,我的原点在左上(0,0),向右向下为正。

cs 复制代码
using System.Collections.Generic;
using UnityEngine;

namespace Pathfinding.AStar
{
    // 最简单的 四向移动矩阵
    public class Dir4AStarAlgorithm
    {
        public int[,] matrix;  // 矩阵
        public Vector2Int start ;  // 起点
        public Vector2Int end ;  // 终点
        private List<AStarNode> openList = new List<AStarNode>();  // 开放列表
        private HashSet<AStarNode> closedList = new HashSet<AStarNode>();  // 关闭列表 ,因为数据结构中的数据来自于不同对象,所以我没有重写Equals/GetHashCode
        private AStarNode[,] nodes;  // 存储所有节点的数组
        
        private Vector2Int[] directions = new Vector2Int[]
        {
            new Vector2Int(0, -1), // 上
            new Vector2Int(1, 0),  // 右
            new Vector2Int(0, 1),  // 下
            new Vector2Int(-1, 0), // 左
        };

        public Dir4AStarAlgorithm(int[,] matrixParam)
        {
            matrix = matrixParam;
            
            int cols = matrix.GetLength(0);
            int rows = matrix.GetLength(1);

            nodes = new AStarNode[cols, rows];
            
            // 初始化节点
            for (int i = 0; i < rows; i++)
            {
                for (int j = 0; j < cols; j++)
                {
                    nodes[j, i] = new AStarNode(j, i);
                    if (matrix[j, i] == 1)
                    {
                        start = new Vector2Int(j, i);
                    }

                    if (matrix[j, i] == 2)
                    {
                        end = new Vector2Int(j, i);
                    }
                }
            }
        }

        public void RunAStar()
        {
            AStarNode startNode = nodes[start.x, start.y];
            AStarNode endNode = nodes[end.x, end.y];
            
            startNode.g = 0;
            startNode.h = CalculateHeuristic(startNode, endNode);
            startNode.CalculateF();
            
            openList.Add(startNode);

            while (openList.Count > 0)
            {
                // 获取 f 值最小的节点
                AStarNode currentFMinNode = GetNodeWithLowestF();
                
                if (currentFMinNode == endNode)
                {
                    // 找到目标,构造路径
                    TracePath(currentFMinNode);
                    return;
                }
                
                openList.Remove(currentFMinNode);
                closedList.Add(currentFMinNode);
                
                // 检查邻接节点
                foreach (var dir in directions)
                {
                    int newX = currentFMinNode.x + dir.x;
                    int newY = currentFMinNode.y + dir.y;

                    // 如果新位置不在矩阵范围内,跳过
                    if (newX < 0 || newX >= matrix.GetLength(0) || newY < 0 || newY >= matrix.GetLength(1))
                        continue;

                    // 如果是障碍,跳过
                    if (matrix[newX, newY] == 3)
                        continue;

                    AStarNode neighbor = nodes[newX, newY];

                    if (closedList.Contains(neighbor))
                        continue;

                    // 计算 g、h 和 f
                    float tentativeG = currentFMinNode.g + 1; // 假设每一步的代价为 1
                    if (tentativeG < neighbor.g)
                    {
                        neighbor.g = tentativeG;
                        neighbor.h = CalculateHeuristic(neighbor, endNode);
                        neighbor.CalculateF();
                        neighbor.parent = currentFMinNode;

                        if (!openList.Contains(neighbor))
                        {
                            openList.Add(neighbor);
                        }
                    }
                }
            }
        }
        
        // 使用曼哈顿距离作为启发式估算
        float CalculateHeuristic(AStarNode node, AStarNode targetNode)
        {
            return Mathf.Abs(node.x - targetNode.x) + Mathf.Abs(node.y - targetNode.y);
        }
        
        AStarNode GetNodeWithLowestF()
        {
            AStarNode lowestNode = openList[0];
            foreach (var node in openList)
            {
                if (node.f < lowestNode.f)
                {
                    lowestNode = node;
                }
            }
            return lowestNode;
        }
        
        void TracePath(AStarNode endNode)
        {
            AStarNode currentNode = endNode;
            while (currentNode != null)
            {
                // 在控制台打印路径
                Debug.Log($"路径: ({currentNode.x}, {currentNode.y})");
                currentNode = currentNode.parent;
            }
        }
    }
}
cs 复制代码
namespace Pathfinding.AStar
{
    public class AStarNode
    {
        public int x, y;
        public float g, h, f;
        public AStarNode parent;
        
        public AStarNode(int x, int y)
        {
            this.x = x;
            this.y = y;
            this.g = float.MaxValue;
            this.h = 0;
            this.f = float.MaxValue;
            this.parent = null;
        }
        
        public void CalculateF()
        {
            f = g + h;
        }
    }
}
相关推荐
Tisfy9 分钟前
LeetCode 3650.边反转的最小路径总成本:Dijkstra算法
算法·leetcode··dijkstra·题解·迪杰斯特拉
2401_8384725111 分钟前
自定义操作符重载指南
开发语言·c++·算法
鹿角片ljp17 分钟前
力扣136.只出现一次的数字-异或和HashMap
java·数据结构·算法·leetcode
W_a_i_T17 分钟前
【Coding日记】菜鸟编程C语言100例——第三题⚠️
c语言·开发语言·经验分享·算法·菜鸟编程
TracyCoder12319 分钟前
LeetCode Hot100(5/100)——11. 盛最多水的容器
算法·leetcode
weixin_4521595519 分钟前
多协议网络库设计
开发语言·c++·算法
你怎么知道我是队长24 分钟前
C语言---排序算法2---选择排序法
c语言·算法·排序算法
啊阿狸不会拉杆25 分钟前
《数字信号处理》第三章 离散傅里叶变换 (DFT)
算法·matlab·深度优先·信号处理·数字信号处理·dsp
gc_229926 分钟前
学习C#调用OpenXml操作word文档的基本用法(20:学习嵌入文件类)
c#·word·openxml·嵌入文档
2301_7886624029 分钟前
C++与微服务架构
开发语言·c++·算法