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;
        }
    }
}
相关推荐
haing20191 天前
七轴协作机器人运动学正解计算方法
算法·机器学习·机器人
谈笑也风生1 天前
把二叉搜索树转换为累加树(一)
算法
youngee111 天前
hot100-64跳跃游戏
算法·游戏
liu****1 天前
机器学习-线性回归
人工智能·python·算法·机器学习·回归·线性回归
智者知已应修善业1 天前
【数组删除重复数据灵活算法可修改保留重复数量】2024-3-4
c语言·c++·经验分享·笔记·算法
你怎么知道我是队长1 天前
C语言---字符串
java·c语言·算法
你怎么知道我是队长1 天前
C语言---指针
c语言·数据结构·算法
汉克老师1 天前
GESP2025年12月认证C++五级真题与解析(编程题2 (相等序列))
c++·算法·贪心算法·中位数·质数分解