百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

最短路径之A算法

toyiye 2024-06-21 12:09 11 浏览 0 评论

在求最短路径问题是,如果去除负权边的情况,可以使用Dijkstra算法来替代贝尔曼-福特算法,复杂度更优。接下来介绍的A*算法,也是一种相对更优的算法。看下图:


示意图

使用Dijkstra算法来计算AB的最短路径,基本上要把所有的点、边都遍历,才能得到最短路径——绿线。但是站在人类的角度来看,蓝线是不需要考虑的,因为有个成语叫“南辕北辙”,我们非常清楚地知道,按照蓝线走,哪怕走一步,都离目标B远了。所以在搜索路径的时候,完全可以抛弃蓝线。等于是在众多分支中,“剪”去一条——这就是“剪枝”。

那么按照什么标准“剪”,就很重要了,这里引出下一个概念——估价函数。

正如之前所说,我们之所以“剪”去蓝线,就是因为我们“估计”走蓝线会离目的地的“距离”更大。所以我们可以通过估价函数,先“估”出起点到终点的距离,在搜索路径的时候,如果某条路线距离目的地的距离越来越大,则可以“剪”去。

当然,估值对算法的影响很大。估大了,比如100万公里,去趟月球再绕回来都有富余;估小了,比如1米,那连门都出不了了。

在A*算法中,常用的距离估价函数就是之前写的三个,不再赘述。下面着重讲A*算法。


示意图

如上图,设尽可上下左右移动,距离估价函数使用曼哈顿距离,边权重统一为10,起点8,终点18,10,16,22为障碍物。

我们要对节点进行松弛操作,也就是将节点的权重降至最小。A*算法相比较贝尔曼-福特算法、Dijkstra算法重要的不同之一就是对权重值的计算,函数如下:

权重值f=起点到节点的移动代价g+节点到终点的预估距离h。

A*算法设置两个集合:open——待处理节点、close——已处理节点。

算法描述:

0、初始化open集合,将起点放入open中,权重为0。
1、遍历open集合
 1.1、如果open为空则说明搜索完成,跳出。
 1.2、不为空,取出权重最小的节点。
  1.2.1、节点是终点,跳出。
  1.2.2、节点非终点。
   1.2.2.1、从open挪入close。
   1.2.2.2、遍历节点的临近节点。
    1.2.2.2.1、临近节点在close中,跳出。
    1.2.2.2.2、临近节点不在open中:
                      计算起点到临近节点的曼哈顿距离g;计算临近节点到终点的曼哈顿距离h;总距离f=g+h;放入open集合中;修改节点的g、h值;记录父节点。
    1.2.2.2.3、临近节点在open中:
                      获取临近节点原g——tmpG;起点经父节点到临近节点的新距离g=(父g+10);
                      如果tmpG<=g,跳出。
                      如果tmpG>g:临近节点新总距离f=g+原h;放入open集合中;修改临近节点的g值;记录父节点。//这一步就是松弛操作,三个距离f、g、h,使用g作比较,是因为节点到终点的距离H是固定的,G越小,说明起点到节点的距离越小,总距离F就越小。

代码如下:

public void getShortestPath(int start, int end) {
        // 初始化节点的父节点Map
        Map<Integer, Integer> parentMap = Maps.newHashMap();
        // 初始化节点路径
        Table<Integer, Integer, Integer> ppw = this.buildPPW();
        // 初始化节点Map
        Map<Integer, Node> nodeMap = this.buildNodeMap();
        nodeMap.get(start).setG(0);
        nodeMap.get(start).setH(Distance.manhattan(nodeMap.get(start), nodeMap.get(end)).intValue());
        // 初始化open集合,将起点放入,权重F为0
        Map<Integer, Integer> openMap = Maps.newHashMap();
        openMap.put(start, 0);
        Map<Integer, Integer> closeMap = Maps.newHashMap();

        while (!openMap.isEmpty()) {
            // 获取权重值最小的节点
            Map.Entry<Integer, Integer> min = this.getMinPoint(openMap);
            // 最小权重节点是终点,跳出
            if (min.getKey().equals(end)) {
                break;
            }
            // 将最小权重节点从open集合移除,加入close集合
            closeMap.put(min.getKey(), min.getValue());
            openMap.remove(min.getKey());

            // 开始遍历min的临近节点。完全可以采用Map<节点,临近节点List>的数据结构
            for (Map.Entry<Integer, Node> entry : nodeMap.entrySet()) {
                // 节点不相邻
                if (Objects.isNull(ppw.get(min.getKey(), entry.getKey()))) {
                    continue;
                }
                // 临近节点在close集合中
                if (closeMap.containsKey(entry.getKey())) {
                    continue;
                }
                // 临近节点不在open集合中
                if (!openMap.containsKey(entry.getKey())) {
                    // 起点到临近节点的距离
                    int g = Distance.manhattan(nodeMap.get(min.getKey()), nodeMap.get(entry.getKey())).intValue();
                    // 临近节点到终点的距离
                    int h = Distance.manhattan(nodeMap.get(entry.getKey()), nodeMap.get(end)).intValue();
                    // 起点经临近节点到终点的总距离
                    int f = g + h;
                    // 放入open集合
                    openMap.put(entry.getKey(), f);
                    // 更改Node的距离值
                    nodeMap.get(entry.getKey()).setG(g);
                    nodeMap.get(entry.getKey()).setH(h);
                    // 记录父节点
                    parentMap.put(entry.getKey(), min.getKey());
                } else {
                    // 临近节点在open集合中
                    // 获取临近节点的原权重G
                    int tmpG = nodeMap.get(entry.getKey()).getG();
                    // 起点经父节点到临近节点的距离。使用G来作比较,是因为节点到终点的距离H是固定的,G越小,说明起点到节点的距离越小,总距离F就越小
                    int g = nodeMap.get(min.getKey()).getG() + 10;
                    // 新值较小
                    if (tmpG > g) {
                        // 临近节点到终点的距离
                        int h = nodeMap.get(entry.getKey()).getH();
                        // 起点经临近节点到终点的总距离
                        int f = g + h;
                        // 放入open集合
                        openMap.put(entry.getKey(), f);
                        // 更改Node的距离值
                        nodeMap.get(entry.getKey()).setG(g);
                        // 记录父节点
                        parentMap.put(entry.getKey(), min.getKey());
                    }
                }
            }
        }
    }

在代码中,Node就4个属性:坐标x、y;距离h、g——初始值都是Integer.MAX_VALUE。不再赘述。

额外要说的是节点路径集合Table<Integer, Integer, Integer> ppw,这个是我沿用以前的代码,并不优化,大家完全可以构建一个Map<节点,临近节点List>替换。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码