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;
        }
    }
}
相关推荐
JAVA面经实录9172 小时前
Java企业级工程化·终极完整版背诵手册(无遗漏、全覆盖、面试+落地通用)
java·开发语言·面试
王老师青少年编程3 小时前
csp信奥赛C++高频考点专项训练之贪心算法 --【哈夫曼贪心】:合并果子
c++·算法·贪心·csp·信奥赛·哈夫曼贪心·合并果子
叼烟扛炮4 小时前
C++第二讲:类和对象(上)
数据结构·c++·算法·类和对象·struct·实例化
天疆说4 小时前
【哈密顿力学】深入解读航天器交会最优控制中的Hamilton函数
人工智能·算法·机器学习
wuweijianlove5 小时前
关于算法设计中的代价函数优化与约束求解的技术7
算法
小程故事多_805 小时前
[大模型面试系列] 多轮对话 Agent 设计实战(含窗口优化 + 工具调用精髓)
人工智能·面试·职场和发展
leoufung5 小时前
LeetCode 149: Max Points on a Line - 解题思路详解
算法·leetcode·职场和发展
样例过了就是过了5 小时前
LeetCode热题100 最长公共子序列
c++·算法·leetcode·动态规划
HXDGCL5 小时前
矩形环形导轨:自动化循环线的核心运动单元解析
运维·算法·自动化
谭欣辰5 小时前
C++ 排列组合完整指南
开发语言·c++·算法