0

0

解决网格路径查找算法中无限循环问题

霞舞

霞舞

发布时间:2025-11-29 11:06:35

|

203人浏览过

|

来源于php中文网

原创

解决网格路径查找算法中无限循环问题

本文旨在解决网格路径查找算法中常见的无限循环问题。通过分析原始代码在路径跟踪和循环检测方面的不足,我们将引入一种基于多路径探索和有效循环避免策略的解决方案。文章将详细阐述如何使用队列管理所有可能的探索路径,并在每一步移动中检查当前路径是否包含目标点,从而确保算法能够高效、准确地找到目标路径,避免陷入重复移动的困境。

理解路径查找算法中的无限循环

在网格或迷宫中进行路径查找时,算法可能会陷入无限循环,即在两个或多个点之间反复移动而无法到达目标。这通常发生在算法未能正确跟踪已访问的节点或未能有效探索所有潜在路径分支时。

原始代码中存在以下几个关键问题,导致了无限循环:

  1. 单路径探索与立即更新 start: 原始实现尝试通过维护一个 start 变量和 tmpPath 队列来跟踪当前路径。然而,在循环内部,一旦找到一个有效的移动方向,start 就会立即更新为 move,并且 break 语句会中断对其他方向的探索。这意味着算法总是沿着第一个发现的有效路径前进,而没有记录或回溯其他可能的路径分支。
  2. 路径跟踪不完整: tmpPath.add(move); path.add(tmpPath.poll()); 这样的操作实际上并没有正确地构建和维护一个完整的路径。tmpPath.poll() 会移除并返回队列的头部元素,而 path.add() 随后添加的可能不是我们期望的当前路径的延伸。
  3. 缺乏循环检测: 算法没有机制来检测当前路径是否已经包含了即将访问的点。如果一个点已经被当前路径访问过,再次访问它就会形成一个循环,导致算法在这些点之间无限往复。

核心路径查找原理:多路径探索与循环避免

为了解决上述问题,我们需要采用一种更健壮的路径查找策略,通常基于广度优先搜索(BFS)或深度优先搜索(DFS)的思想。其核心在于:

  1. 管理所有潜在路径: 不仅仅跟踪一个“当前”点,而是维护一个集合,其中包含所有从起点到当前已探索到的点的完整路径。
  2. 逐一探索路径: 从这个集合中取出一条路径进行探索。
  3. 生成新路径: 对于当前路径的最后一个点,探索其所有可能的邻居。对于每一个有效的、未在当前路径中出现过的邻居,生成一条新的路径(即在当前路径的基础上添加这个邻居),并将其添加到待探索的路径集合中。
  4. 循环检测: 在生成新路径时,必须检查新的点是否已经存在于当前路径中。如果存在,则说明会形成循环,应避免将该点添加到当前路径。

实现一个健壮的路径查找算法

我们将修改原始代码,采用多路径探索和循环避免的策略。这里我们选择使用 ArrayDeque 来存储多条路径,并通过 removeLast() 实现类似深度优先搜索(DFS)的行为。

Vondy
Vondy

下一代AI应用平台,汇集了一流的工具/应用程序

下载
import java.awt.Color; // 假设 Color 类存在于此
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.Queue; // 虽然原代码用Queue,但这里我们用Deque作为栈或队列

// 假设 Point 和 Rectangle 类定义如下,并包含 isValid 方法
// class Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } boolean isValid(int gridWidth, int gridHeight) { return x >= 0 && x < gridWidth && y >= 0 && y < gridHeight; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Point point = (Point) o; return x == point.x && y == point.y; } @Override public int hashCode() { return Objects.hash(x, y); } @Override public String toString() { return "(" + x + ", " + y + ")"; } }
// class Rectangle { Color fill; public Color getFill() { return fill; } }

public class PathFinder {

    // 假设 Point 类有一个 x() 和 y() 方法,对应 x 和 y 坐标
    // 为了与原始代码兼容,我们假设 Point 是一个记录(record)或有一个公共的 x, y 字段
    // 如果 Point 是一个 record class Point(int x, int y) {}
    // 如果是普通类,需要 Point.x 和 Point.y
    // 这里为了演示,我们假设 Point 有 x() 和 y() 方法
    // 实际项目中请根据 Point 类的具体实现进行调整
    private static class Point {
        int x, y;
        public Point(int x, int y) { this.x = x; this.y = y; }
        public int x() { return x; }
        public int y() { return y; }
        public boolean isValid(int gridWidth, int gridHeight) {
            return x >= 0 && x < gridWidth && y >= 0 && y < gridHeight;
        }
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Point point = (Point) o;
            return x == point.x && y == point.y;
        }
        @Override
        public int hashCode() {
            return java.util.Objects.hash(x, y);
        }
        @Override
        public String toString() {
            return "(" + x + ", " + y + ")";
        }
    }

    // 假设 Rectangle 类有一个 getFill() 方法返回 Color
    private static class Rectangle {
        Color fill;
        public Rectangle(Color fill) { this.fill = fill; }
        public Color getFill() { return fill; }
    }


    public Deque findPath(Rectangle[][] matrix, Point destPoint) {
        // 定义移动方向:右、下、左、上
        var dir = new ArrayList();
        dir.add(new Point(1, 0)); // right
        dir.add(new Point(0, 1)); // down
        dir.add(new Point(-1, 0)); // left
        dir.add(new Point(0, -1)); // up

        Point start = new Point(0, 0); // 起始点固定为 (0,0)

        // availablePaths 存储所有待探索的完整路径
        // 每个元素本身是一个 Deque,代表一条从起点到当前点的完整路径
        Deque> availablePaths = new ArrayDeque<>();

        // 初始化:将起始点作为第一条路径加入待探索集合
        Deque initialPath = new ArrayDeque<>();
        initialPath.add(start);
        availablePaths.add(initialPath);

        // 当还有待探索的路径时,循环继续
        while (!availablePaths.isEmpty()) {
            // 从 availablePaths 中取出一个路径进行探索
            // removeLast() 使得它表现得像一个栈,实现深度优先搜索 (DFS)
            // 如果使用 removeFirst(),则实现广度优先搜索 (BFS)
            Deque currentPath = availablePaths.removeLast();
            Point currentPoint = currentPath.getLast(); // 获取当前路径的最后一个点

            // 检查是否已到达目标点
            if (currentPoint.equals(destPoint)) {
                return currentPath; // 找到路径,返回
            }

            // 探索当前点的所有可能邻居
            for (int dc = 0; dc < dir.size(); dc++) {
                Point nextMove = new Point(currentPoint.x() + dir.get(dc).x(), currentPoint.y() + dir.get(dc).y());

                // 检查移动是否在网格范围内
                if (!nextMove.isValid(matrix[0].length, matrix.length)) {
                    continue;
                }

                // 检查新点是否已经在当前路径中,避免形成循环
                if (currentPath.contains(nextMove)) {
                    continue;
                }

                // 检查新点是否是墙壁 (MAGENTA)
                if (matrix[nextMove.y()][nextMove.x()].getFill() != Color.MAGENTA) {
                    // 如果是有效且未访问过的点,则创建一条新路径
                    // 新路径是当前路径的副本,并添加新点
                    Deque newPath = new ArrayDeque<>(currentPath);
                    newPath.add(nextMove);
                    availablePaths.add(newPath); // 将新路径加入待探索集合
                }
            }
        }

        // 如果所有路径都探索完毕仍未找到目标,则返回 null
        return null;
    }
}

代码修改详解

  1. Deque> availablePaths: 这是最核心的改变。它不再是一个简单的 Queue,而是一个 Deque,其中每个元素本身又是一个 Deque。外层的 Deque 存储了所有从起点到当前已探索到的点的完整路径。
  2. 初始化 availablePaths:
    • Deque initialPath = new ArrayDeque(); initialPath.add(start);:创建一条只包含起始点的路径。
    • availablePaths.add(initialPath);:将这条初始路径添加到待探索集合中。
  3. 循环逻辑 while (!availablePaths.isEmpty()):
    • Deque currentPath = availablePaths.removeLast();:每次循环取出一条完整的路径。removeLast() 实现了类似 DFS 的探索方式,优先探索最新生成的路径分支。如果改为 removeFirst(),则会实现 BFS。
    • Point currentPoint = currentPath.getLast();:获取当前路径的最后一个点,即当前正在探索的点。
  4. 目标检测 if (currentPoint.equals(destPoint)): 提前到循环开始处进行检查,一旦取出路径的最后一个点是目标点,则立即返回该路径。
  5. 循环避免 if (currentPath.contains(nextMove)): 这是解决无限循环的关键。在探索 nextMove 之前,检查 currentPath 是否已经包含 nextMove。如果包含,说明沿着当前路径走会形成一个循环,因此跳过这个移动。
  6. 生成新路径 Deque newPath = new ArrayDeque(currentPath); newPath.add(nextMove);:
    • 每次找到一个有效的新点时,不是简单地更新 start,而是创建一个 currentPath 的副本。
    • 将 nextMove 添加到这个副本中,形成一条新的、更长的完整路径。
    • availablePaths.add(newPath);:将这条新路径加入到待探索集合中。
  7. 移除 break 语句: 原始代码在找到第一个有效移动后就 break 了,这限制了算法的探索能力。在新代码中,我们会遍历所有可能的方向,为每个有效方向生成一条新路径,并将其加入 availablePaths,从而确保所有分支都能被探索到。

总结与注意事项

通过上述修改,路径查找算法变得更加鲁棒和高效:

  • 避免无限循环: currentPath.contains(nextMove) 确保了算法不会在同一条路径上重复访问节点,从而有效防止了无限循环。
  • 完整路径探索: availablePaths 存储了所有从起点到当前点的完整路径,确保了算法可以回溯并探索不同的分支。
  • 灵活性: 通过修改 availablePaths.removeLast() 为 removeFirst(),可以轻松地在深度优先搜索和广度优先搜索之间切换,以适应不同的需求(例如,BFS 找到的是最短路径,DFS 可能找到的不是最短路径,但通常占用更少的内存)。

在实际应用中,对于大型网格,为了优化性能,可以引入一个 visited 集合来存储所有已经被某个路径探索过的节点,这样可以避免重复计算,但需要注意的是,visited 集合通常用于记录 所有 已访问的节点,而 path.contains() 仅检查 当前路径 中的节点,两者作用不同,在某些复杂场景下可能需要同时使用。

这个改进后的算法能够可靠地在网格中找到从起点到目标点的路径,并有效避免陷入无限循环的困境。

相关专题

更多
if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

737

2023.08.22

while的用法
while的用法

while的用法是“while 条件: 代码块”,条件是一个表达式,当条件为真时,执行代码块,然后再次判断条件是否为真,如果为真则继续执行代码块,直到条件为假为止。本专题为大家提供while相关的文章、下载、课程内容,供大家免费下载体验。

85

2023.09.25

java中break的作用
java中break的作用

本专题整合了java中break的用法教程,阅读专题下面的文章了解更多详细内容。

118

2025.10.15

java break和continue
java break和continue

本专题整合了java break和continue的区别相关内容,阅读专题下面的文章了解更多详细内容。

255

2025.10.24

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

400

2023.08.14

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

34

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

14

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

33

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

18

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 2.5万人学习

C# 教程
C# 教程

共94课时 | 6.7万人学习

Java 教程
Java 教程

共578课时 | 45.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号