【路径规划】A*算法 Java实现

news2024/9/23 21:20:59

A*(A-Star)算法是一种广泛使用的寻路算法,尤其在计算机科学和人工智能领域。

算法思想

通过评估函数来引导搜索过程,从而找到从起始点到目标点的最短路径。评估函数通常包括两部分:一部分是已经走过的实际距离,称为g值;另一部分是对当前位置到目标位置的估计距离,称为h值。A*算法每次选择g值加上h值最小的节点作为下一个要访问的节点,直到找到目标节点为止。

A*算法维护一个开放列表和一个关闭列表。开放列表包含待访问的节点,而关闭列表包含已经访问过的节点。算法从起始节点开始,将其加入开放列表。然后,算法从开放列表中选择一个评估函数值最小的节点进行扩展,将其邻居节点加入开放列表,并将当前节点移入关闭列表。算法不断重复这个过程,直到找到目标节点或者开放列表为空。

在这里插入图片描述

代码实现

单元格类实现

/**
 * 表示地图上的一个单元格
 */
class Cell {
    int i, j; // 单元格的行和列索引
    int f, g, h; // f值、g值和h值
    Cell parent; // 父节点
    boolean closed, visited; // 是否关闭和是否访问过

    // 构造函数
    public Cell(int i, int j) {
        this.i = i;
        this.j = j;
        this.f = 0;
        this.g = 0;
        this.h = 0;
        this.parent = null;
        this.closed = false;
        this.visited = false;
    }
}

A*算法类实现

/**
 * A*算法实现类
 */
public class AStar {
    private int[][] map; // 地图
    private int startI, startJ; // 起始位置的行和列索引
    private int endI, endJ; // 目标位置的行和列索引
    private List<Cell> openList = new ArrayList<>(); // 开放列表
    private static final int DIAGONAL_COST = 14; // 对角线移动的代价
    private static final int VERTICAL_HORIZONTAL_COST = 10; // 垂直或水平移动的代价

    // 构造函数
    public AStar(int[][] map, int startI, int startJ, int endI, int endJ) {
        this.map = map;
        this.startI = startI;
        this.startJ = startJ;
        this.endI = endI;
        this.endJ = endJ;
    }

    /**
     * 搜索路径
     */
    public void search() {
        // 创建起始和目标单元格对象
        Cell start = new Cell(startI, startJ);
        Cell end = new Cell(endI, endJ);
        // 将起始单元格添加到开放列表中
        openList.add(start);
        while (!openList.isEmpty()) {
            // 按照f值对开放列表进行排序,选择f值最小的单元格作为当前单元格
            Collections.sort(openList, Comparator.comparingInt(cell -> cell.f));
            Cell current = openList.get(0);
            // 如果当前单元格是目标单元格,则找到路径,打印路径并返回
            if (current.i == end.i && current.j == end.j) {
                printPath(current);
                return;
            }
            // 从开放列表中移除当前单元格,并将其标记为已关闭
            openList.remove(current);
            current.closed = true;
            // 遍历邻居单元格
            for (int[] direction : directions) {
                int newI = current.i + direction[0]; // 计算新的行索引
                int newJ = current.j + direction[1]; // 计算新的列索引
                // 如果新的索引越界,则跳过该邻居单元格的处理
                if (newI < 0 || newI >= map.length || newJ < 0 || newJ >= map[0].length) {
                    continue;
                }
                // 如果邻居单元格是障碍物,则跳过该邻居单元格的处理
                if (map[newI][newJ] == 1) {
                    continue;
                }
                Cell neighbor = new Cell(newI, newJ);
                if (neighbor.closed) { // 已关闭的单元格处理
                    continue;
                }
                // 计算代价和启发式函数值
                int g = current.g + getCost(current, neighbor);
                int h = heuristic(neighbor, end);
                if (!neighbor.visited) { // 未访问过的邻居单元格处理
                    neighbor.visited = true;
                    neighbor.parent = current; // 设置父节点
                    neighbor.g = g; // 设置g值
                    neighbor.h = h; // 设置h值
                    neighbor.f = g + h; // 设置f值
                    openList.add(neighbor); // 添加到开放列表中
                } else if (g < neighbor.g) { // 已访问过的邻居单元格,且新的路径代价更小处理
                    neighbor.parent = current; // 更新父节点
                    neighbor.g = g; // 更新g值
                    neighbor.f = g + neighbor.h; // 更新f值
                }
            }
        }
        System.out.println("No path found."); // 没有找到路径的情况处理
    }

    // 计算从当前单元格到邻居单元格的代价
    private int getCost(Cell current, Cell neighbor) {
        int dx = Math.abs(current.i - neighbor.i);
        int dy = Math.abs(current.j - neighbor.j);
        if (dx == 1 && dy == 1) { // 对角线移动
            return DIAGONAL_COST;
        } else { // 垂直或水平移动
            return VERTICAL_HORIZONTAL_COST;
        }
    }

    // 启发式函数,计算当前单元格到目标单元格的预计代价
    private int heuristic(Cell current, Cell goal) {
        int dx = Math.abs(current.i - goal.i);
        int dy = Math.abs(current.j - goal.j);
        return dx + dy; // 使用曼哈顿距离作为启发式函数
    }

    // 打印路径
    private void printPath(Cell end) {
        List<Cell> path = new ArrayList<>();
        Cell current = end;
        while (current != null) {
            path.add(current);
            current = current.parent;
        }
        Collections.reverse(path);
        System.out.print("Path: ");
        for (Cell cell : path) {
            System.out.print("(" + cell.i + "," + cell.j + ") ");
        }
        System.out.println();
    }

    // 八个方向的移动向量
    private static final int[][] directions = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};

    public static void main(String[] args) {
        int[][] map = {{0, 0, 0, 0, 0}, {0, 1, 1, 0, 0}, {0, 0, 0, 0, 0}, {0, 1, 1, 1, 0}, {0, 0, 0, 0, 0}}; // 定义地图,0表示可通过,1表示障碍物
        //打印一次地图用于观察
        for (int[] arr : map) {
            for (int x : arr) {
                System.out.print(x + "   ");
            }
            System.out.println();
        }
        AStar astar = new AStar(map, 0, 0, 4, 4); // 创建A*对象,设置起始位置和目标位置
        astar.search(); // 搜索路径
    }
}

以下是它的优点和缺点:

优点

  • 完整性:A* 算法总是能找到从起始点到目标点的最短路径,只要这样的路径存在。
  • 最优性:A* 算法找到的路径是最短的,或者说代价最小的。
  • 启发式搜索:A* 算法采用启发式函数来引导搜索过程,提高了搜索效率。

缺点

  • 空间复杂度较高:A* 算法需要存储搜索过程中的所有节点,因此在复杂地图或大数据集上运行时,可能会占用大量内存。
  • 时间复杂度较高:尽管 A* 算法比许多其他搜索算法更高效,但在大规模问题中,搜索时间可能会变得很长。
  • 对启发式函数依赖性强:A* 算法的效率在很大程度上取决于选择的启发式函数。如果启发式函数选择不当,可能会导致搜索效率低下。

以上就是 A* 算法的优缺点,需要根据具体的应用场景来决定是否使用 A* 算法。

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

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

相关文章

「我在淘天做技术」双 11 背后的营销技术体系

作者&#xff1a;朱咏杰(小枫) 近期淘天集团秋季 2024 届校园招聘正式启动&#xff0c;预计将发放 2000 多个 offer&#xff0c;其中技术类岗位占比超过 50%。为了方便大家更真实地了解淘天技术的布局和现状&#xff0c;我们策划了「我在淘天做技术」系列&#xff0c;首次全面分…

科技资讯|苹果穿戴新专利,表带、服装等织物可变身柔性屏幕或扬声器

根据美国商标和专利局&#xff08;USPTO&#xff09;本周公示的清单&#xff0c;苹果公司获得了一项新的技术专利&#xff0c;可以在 Apple Watch 表带、服装等物品上&#xff0c;引入基于织物的柔性扬声器。 根据专利描述&#xff0c;通过在织物中嵌入声学组件&#xff08;例…

Makefile总结

一、Makefile用法及变量&#xff08;自定义变量、自动变量、隐含变量&#xff09; 一、Makefile的重要性 1、编译文件 2、正常编译&#xff0c;文件多的时候操作麻烦 3、决定能不能完成大型工程 二、Makefile的概述 1、自动化编译-makefile 编译效率&#xff1a;make编译…

01.MySQL(SQL分类及使用)

注意&#xff1a;DML只是进行增删改&#xff0c;DQL才有查询 分类全称说明DDLData Definition Language数据定义语言&#xff0c;用来定义数据库对象&#xff08;数据库&#xff0c;表&#xff0c;字段&#xff09;DMLData Manipulation Language数据操作语言&#xff0c;用来…

vue3的getCurrentInstance获取组件实例踩坑记录

一、getCurrentInstance基本用法 我们可以通过 getCurrentInstance这个函数来返回当前组件的实例对象,也就是当前vue这个实例对象 Vue2中&#xff0c;可以通过this来获取当前组件实例&#xff1b; Vue3中&#xff0c;在setup中无法通过this获取组件实例&#xff0c;console.lo…

ElasticSearch中关于Nasted嵌套查询的介绍:生动案例,通俗易懂,彻底吸收

题注&#xff1a;随着对ES接触的越来越深入&#xff0c;发现此前了解的ES知识点有点单薄&#xff0c;特此寻来ES知识点汇总成的一个思维导图&#xff0c;全面了解自己掌握了哪些&#xff0c;未掌握哪些。此外&#xff0c;作者斌并没有足够的精力学习ES全部的知识点&#xff0c;…

1024程序员节,飞桨星河社区开发者们一起闯关升级、玩转Prompt应用赢大奖~

1024&#xff0c;是属于每一位程序员/程序媛的节日~ 今年&#xff0c;飞桨给星河社区的开发者们也准备了“超级码力 碰撞未来”系列活动&#xff0c;和大家沉浸式玩转闯关冒险。 冲榜单 零代码打造爆款Prompt应用 飞桨AI Studio星河社区上线新版文心一言专区&#xff0c;帮助…

代码随想录算法训练营第三十三天 | LeetCode 1005. K 次取反后最大化的数组和、134. 加油站、135. 分发糖果

代码随想录算法训练营第三十三天 | LeetCode 1005. K 次取反后最大化的数组和、134. 加油站、135. 分发糖果 文章链接&#xff1a;K次取反后最大化的数组和 加油站 分发糖果 视频链接&#xff1a;K次取反后最大化的数组和 加油站 分发糖果 目录 代…

STM TIM(二)输出比较

STM TIM&#xff08;二&#xff09;输出比较 输出比较简介 OC&#xff08;Output Compare&#xff09;输出比较 输出比较可以通过比较CNT&#xff08;CNT计数器&#xff09;与CCR寄存器&#xff08;捕获/比较寄存器&#xff09;值的关系&#xff0c;来对输出电平进行置1、置0…

Camtasia2024中文免费版电脑录屏软件

真的要被录屏软件给搞疯了&#xff0c;本来公司说要给新人做个培训视频&#xff0c;想着把视频录屏一下&#xff0c;然后简单的剪辑一下就可以了。可谁知道录屏软件坑这么多&#xff0c;弄来弄去头都秃了&#xff0c;不过在头秃了几天之后&#xff0c;终于让我发现了一个值得“…

如何理解Go言中的Context?

目前看过除了《go语言程序设计》以外最好的教程&#xff1a;https://www.practical-go-lessons.com 原文&#xff1a;https://www.practical-go-lessons.com/chap-37-context 你将在本章中学到什么&#xff1f; 1.什么是上下文&#xff1f; 2.什么是链表&#xff1f; 3.如何…

DAOS学习笔记及思考

DAOS带来的思考 根据daos docs的描述&#xff0c;DAOS是Intel基于NVMe全新设计开发并开源的异步对象存储&#xff0c;充分利用下一代NVMe技术的优势&#xff0c;对外提供KV存储接口&#xff0c;提供非阻塞事物I/O&#xff0c;端到端完整性&#xff0c;细粒度的数据控制&#x…

班级信息收集小程序

老师们&#xff01;这里有一个超级实用的班级信息收集小程序&#xff0c;让你告别繁琐的手动记录成绩&#xff0c;轻松实现学生自助查询成绩&#xff01;是不是很期待&#xff1f; 什么是成绩查询系统&#xff1f; 成绩查询系统是一种基于互联网和数据库技术的应用程序&#x…

基于springboot+vue实现MOBA类游戏攻略平台项目【项目源码+论文说明】

基于springbootvue实现MOBA类游戏攻略平台 摘要 随着信息技术和网络技术的飞速发展&#xff0c;人类已进入全新信息化时代&#xff0c;传统管理技术已无法高效&#xff0c;便捷地管理信息。为了迎合时代需求&#xff0c;优化管理效率&#xff0c;各种各样的管理系统应运而生&a…

HTML5+CSS3+JS小实例:打散文字随机浮动特效

实例:打散文字随机浮动特效 技术栈:HTML+CSS+JS 效果: 源码: 【HTML+JS】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" conte…

SRS Config 二 Stream Caster

SRS StreamCaster 1 官网简介 Stream Converter侦听特殊的TCP/UDP端口&#xff0c;接受客户端连接和媒体流&#xff0c;并转成RTMP流&#xff0c;推送给SRS。 简单来说&#xff0c;它将其他流转成RTMP流&#xff0c;工作流如下&#xff1a; Client ---PUSH--> Stream Co…

微信小程序云开发笔记-初始化商城小程序

一 下载小程序工具 下载地址 二 创建小程序 三 初始化小程序 1 把cloudfunctions文件夹内所有文件删除 2 把miniprogram\components下所有文件删除 3 pages文件夹里面只保留index文件夹&#xff0c;其他都删除并修改index文件夹下文件 index.js 把数据清空&#xff0c;只保…

GEAR框架: Tractian的敏捷工程文化

GEAR(齿轮)框架是工业初创公司TRACTIAN提出的敏捷开发框架&#xff0c;强调一切以人为中心&#xff0c;客户需求为最高优先级&#xff0c;互动胜于流程的开发文化。原文: The GEAR Framework — Tractian’s Agile Engineering Culture GEAR框架&#xff0c;由TRACTIAN和Pietro…

国外调查问卷项目赚美金是真的吗?

大家好&#xff0c;我是橙河网络&#xff0c;一家问卷公司老板&#xff0c;这几年国外问卷这个项目比较火热&#xff0c;很多人都靠这个项目赚的盆满钵满&#xff0c;这篇文章就详细介绍一下国外调查问卷项目赚美金是真的吗&#xff1f; 国外问卷调查是一种付费的市场调研方法…